diff --git a/.gitignore b/.gitignore
index fd0d7207..ce4b2f33 100644
--- a/.gitignore
+++ b/.gitignore
@@ -121,3 +121,6 @@ log.txt
log.txt.meta
Server~/build/*
Server~/node_modules/*
+
+# Claude settings
+.claude/settings.local.json
diff --git a/Editor/Services/ConsoleLogsService.cs b/Editor/Services/ConsoleLogsService.cs
index 1dbbf01d..d7179cf8 100644
--- a/Editor/Services/ConsoleLogsService.cs
+++ b/Editor/Services/ConsoleLogsService.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Reflection;
+using System.Text.RegularExpressions;
using Newtonsoft.Json.Linq;
using UnityEditor;
using UnityEngine;
@@ -256,6 +257,143 @@ private void OnLogMessageReceived(string logString, string stackTrace, LogType t
}
}
+ ///
+ /// Search logs with keyword or regex pattern
+ ///
+ public JObject SearchLogsAsJson(string keyword = null, string regex = null, string logType = null,
+ bool includeStackTrace = true, bool caseSensitive = false, int offset = 0, int limit = 50)
+ {
+ // Prepare search criteria
+ bool hasSearchCriteria = !string.IsNullOrEmpty(keyword) || !string.IsNullOrEmpty(regex);
+ Regex searchRegex = null;
+ string searchKeyword = keyword;
+
+ // If regex is provided, use it instead of keyword
+ if (!string.IsNullOrEmpty(regex))
+ {
+ try
+ {
+ searchRegex = new Regex(regex, caseSensitive ? RegexOptions.None : RegexOptions.IgnoreCase);
+ }
+ catch (ArgumentException ex)
+ {
+ return new JObject
+ {
+ ["logs"] = new JArray(),
+ ["error"] = $"Invalid regex pattern: {ex.Message}",
+ ["success"] = false
+ };
+ }
+ }
+ else if (!string.IsNullOrEmpty(keyword) && !caseSensitive)
+ {
+ searchKeyword = keyword.ToLower();
+ }
+
+ // Map MCP log types to Unity log types
+ HashSet unityLogTypes = null;
+ if (!string.IsNullOrEmpty(logType))
+ {
+ if (LogTypeMapping.TryGetValue(logType, out var mapped))
+ {
+ unityLogTypes = mapped;
+ }
+ else
+ {
+ unityLogTypes = new HashSet(StringComparer.OrdinalIgnoreCase) { logType };
+ }
+ }
+
+ JArray logsArray = new JArray();
+ int totalCount = 0;
+ int filteredCount = 0;
+ int matchedCount = 0;
+ int currentIndex = 0;
+
+ lock (_logEntries)
+ {
+ totalCount = _logEntries.Count;
+
+ // Search through logs (newest first)
+ for (int i = _logEntries.Count - 1; i >= 0; i--)
+ {
+ var entry = _logEntries[i];
+
+ // Skip if filtering by log type and entry doesn't match
+ if (unityLogTypes != null && !unityLogTypes.Contains(entry.Type.ToString()))
+ continue;
+
+ filteredCount++;
+
+ // Check if entry matches search criteria
+ bool matches = true;
+ if (hasSearchCriteria)
+ {
+ matches = false;
+
+ // Search in message
+ if (searchRegex != null)
+ {
+ matches = searchRegex.IsMatch(entry.Message);
+ if (!matches && includeStackTrace && !string.IsNullOrEmpty(entry.StackTrace))
+ {
+ matches = searchRegex.IsMatch(entry.StackTrace);
+ }
+ }
+ else if (!string.IsNullOrEmpty(searchKeyword))
+ {
+ string messageToSearch = caseSensitive ? entry.Message : entry.Message.ToLower();
+ matches = messageToSearch.Contains(searchKeyword);
+
+ if (!matches && includeStackTrace && !string.IsNullOrEmpty(entry.StackTrace))
+ {
+ string stackTraceToSearch = caseSensitive ? entry.StackTrace : entry.StackTrace.ToLower();
+ matches = stackTraceToSearch.Contains(searchKeyword);
+ }
+ }
+ }
+
+ if (!matches) continue;
+
+ matchedCount++;
+
+ // Check if we're in the offset range and haven't reached the limit yet
+ if (currentIndex >= offset && logsArray.Count < limit)
+ {
+ var logObject = new JObject
+ {
+ ["message"] = entry.Message,
+ ["type"] = entry.Type.ToString(),
+ ["timestamp"] = entry.Timestamp.ToString("yyyy-MM-dd HH:mm:ss.fff")
+ };
+
+ // Only include stack trace if requested
+ if (includeStackTrace)
+ {
+ logObject["stackTrace"] = entry.StackTrace;
+ }
+
+ logsArray.Add(logObject);
+ }
+
+ currentIndex++;
+
+ // Early exit if we've collected enough logs
+ if (currentIndex >= offset + limit) break;
+ }
+ }
+
+ return new JObject
+ {
+ ["logs"] = logsArray,
+ ["_totalCount"] = totalCount,
+ ["_filteredCount"] = filteredCount,
+ ["_matchedCount"] = matchedCount,
+ ["_returnedCount"] = logsArray.Count,
+ ["success"] = true
+ };
+ }
+
#if UNITY_6000_0_OR_NEWER
///
/// Called when the console logs count changes
diff --git a/Editor/Services/ConsoleLogsServiceUnity6.cs b/Editor/Services/ConsoleLogsServiceUnity6.cs
new file mode 100644
index 00000000..2beab34b
--- /dev/null
+++ b/Editor/Services/ConsoleLogsServiceUnity6.cs
@@ -0,0 +1,578 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Text.RegularExpressions;
+using Newtonsoft.Json.Linq;
+using UnityEditor;
+using UnityEngine;
+
+namespace McpUnity.Services
+{
+ ///
+ /// Unity 6 specific implementation using ConsoleWindowUtility API
+ /// This implementation uses Unity's internal console APIs for more reliable log retrieval
+ ///
+ public class ConsoleLogsServiceUnity6 : IConsoleLogsService
+ {
+ // Static mapping for MCP log types to Unity log types
+ private static readonly Dictionary> LogTypeMapping = new Dictionary>(StringComparer.OrdinalIgnoreCase)
+ {
+ { "info", new HashSet(StringComparer.OrdinalIgnoreCase) { "Log" } },
+ { "error", new HashSet(StringComparer.OrdinalIgnoreCase) { "Error", "Exception", "Assert" } },
+ { "warning", new HashSet(StringComparer.OrdinalIgnoreCase) { "Warning" } }
+ };
+
+ // Reflection cache for internal Unity APIs
+ private static Type _logEntriesType;
+ private static MethodInfo _getCountMethod;
+ private static MethodInfo _getEntryInternalMethod;
+ private static MethodInfo _startGettingEntriesMethod;
+ private static MethodInfo _endGettingEntriesMethod;
+ private static Type _logEntryType;
+ private static FieldInfo _messageField;
+ private static FieldInfo _fileField;
+ private static FieldInfo _lineField;
+ private static FieldInfo _modeField;
+ private static FieldInfo _callstackTextStartUTF8Field;
+ private static FieldInfo _callstackTextStartUTF16Field;
+
+ static ConsoleLogsServiceUnity6()
+ {
+ InitializeReflection();
+ }
+
+ private static void InitializeReflection()
+ {
+ // Get LogEntries type
+ _logEntriesType = Type.GetType("UnityEditor.LogEntries,UnityEditor");
+ if (_logEntriesType == null)
+ {
+ Debug.LogError("[MCP Unity] Failed to find LogEntries type");
+ return;
+ }
+
+ // Get LogEntry type
+ _logEntryType = Type.GetType("UnityEditor.LogEntry,UnityEditor");
+ if (_logEntryType == null)
+ {
+ Debug.LogError("[MCP Unity] Failed to find LogEntry type");
+ return;
+ }
+
+ // Get methods
+ _getCountMethod = _logEntriesType.GetMethod("GetCount",
+ BindingFlags.Public | BindingFlags.Static | BindingFlags.NonPublic);
+
+ _startGettingEntriesMethod = _logEntriesType.GetMethod("StartGettingEntries",
+ BindingFlags.Public | BindingFlags.Static | BindingFlags.NonPublic);
+
+ _endGettingEntriesMethod = _logEntriesType.GetMethod("EndGettingEntries",
+ BindingFlags.Public | BindingFlags.Static | BindingFlags.NonPublic);
+
+ _getEntryInternalMethod = _logEntriesType.GetMethod("GetEntryInternal",
+ BindingFlags.Public | BindingFlags.Static | BindingFlags.NonPublic);
+
+ // Get fields
+ _messageField = _logEntryType.GetField("message", BindingFlags.Public | BindingFlags.Instance);
+ _fileField = _logEntryType.GetField("file", BindingFlags.Public | BindingFlags.Instance);
+ _lineField = _logEntryType.GetField("line", BindingFlags.Public | BindingFlags.Instance);
+ _modeField = _logEntryType.GetField("mode", BindingFlags.Public | BindingFlags.Instance);
+ _callstackTextStartUTF8Field = _logEntryType.GetField("callstackTextStartUTF8", BindingFlags.Public | BindingFlags.Instance);
+ _callstackTextStartUTF16Field = _logEntryType.GetField("callstackTextStartUTF16", BindingFlags.Public | BindingFlags.Instance);
+ }
+
+ public void StartListening()
+ {
+ // Unity 6: Register for console changes
+ ConsoleWindowUtility.consoleLogsChanged += OnConsoleLogsChanged;
+ }
+
+ public void StopListening()
+ {
+ // Unity 6: Unregister from console changes
+ ConsoleWindowUtility.consoleLogsChanged -= OnConsoleLogsChanged;
+ }
+
+ private void OnConsoleLogsChanged()
+ {
+ // This is called whenever console logs change:
+ // - New logs added
+ // - Console cleared
+ // - Logs filtered/collapsed
+
+ ConsoleWindowUtility.GetConsoleLogCounts(out int error, out int warning, out int log);
+ int totalLogs = error + warning + log;
+
+ if (totalLogs == 0)
+ {
+ Debug.Log("[MCP Unity] Console cleared detected via Unity 6 API");
+ }
+
+ // Since we query Unity directly, we don't need to maintain our own cache
+ // This event just helps us know when to notify clients that log state changed
+ }
+
+ public JObject GetLogsAsJson(string logType = "", int offset = 0, int limit = 100, bool includeStackTrace = true)
+ {
+ if (_logEntriesType == null || _getCountMethod == null || _getEntryInternalMethod == null)
+ {
+ return new JObject
+ {
+ ["logs"] = new JArray(),
+ ["message"] = "LogEntries API not available",
+ ["success"] = false
+ };
+ }
+
+ JArray logsArray = new JArray();
+
+ try
+ {
+ // Get console log counts using Unity 6 API
+ ConsoleWindowUtility.GetConsoleLogCounts(out int errorCount, out int warningCount, out int logCount);
+ int totalCount = errorCount + warningCount + logCount;
+
+ // Map MCP log types to Unity log types
+ HashSet unityLogTypes = null;
+ bool filter = !string.IsNullOrEmpty(logType);
+ if (filter)
+ {
+ if (LogTypeMapping.TryGetValue(logType, out var mapped))
+ {
+ unityLogTypes = mapped;
+ }
+ else
+ {
+ unityLogTypes = new HashSet(StringComparer.OrdinalIgnoreCase) { logType };
+ }
+ }
+
+ // Start getting entries
+ _startGettingEntriesMethod?.Invoke(null, null);
+
+ int currentCount = (int)_getCountMethod.Invoke(null, null);
+ var collectedLogs = new List();
+
+ // Iterate through all logs (newest first)
+ for (int i = currentCount - 1; i >= 0; i--)
+ {
+ // Create LogEntry instance
+ var logEntry = Activator.CreateInstance(_logEntryType);
+
+ // GetEntryInternal(int row, LogEntry outputEntry)
+ bool success = (bool)_getEntryInternalMethod.Invoke(null, new object[] { i, logEntry });
+
+ if (!success) continue;
+
+ // Extract fields (see Unity6InternalAPIReference.md for field details)
+ string fullMessage = _messageField?.GetValue(logEntry) as string ?? "";
+ string file = _fileField?.GetValue(logEntry) as string ?? "";
+ int line = _lineField?.GetValue(logEntry) as int? ?? 0;
+ int mode = _modeField?.GetValue(logEntry) as int? ?? 0;
+ int callstackStartUTF8 = _callstackTextStartUTF8Field?.GetValue(logEntry) as int? ?? 0;
+ int callstackStartUTF16 = _callstackTextStartUTF16Field?.GetValue(logEntry) as int? ?? 0;
+
+ // Debug: Write mode values to file for analysis (disabled by default)
+ #if MCP_UNITY_DEBUG_MODE_VALUES
+ if (fullMessage.Contains("error") || fullMessage.Contains("Error") ||
+ fullMessage.Contains("warning") || fullMessage.Contains("Warning") ||
+ fullMessage.Contains("failed") || fullMessage.Contains("Failed") ||
+ fullMessage.Contains("exception") || fullMessage.Contains("Exception"))
+ {
+ WriteDebugInfo(fullMessage, mode);
+ }
+ #endif
+
+ // Parse message and stack trace using Unity's internal callstack position (prefer UTF-16)
+ var (actualMessage, stackTrace) = ParseMessageAndStackTrace(fullMessage, callstackStartUTF16, callstackStartUTF8);
+
+ // Determine log type from mode and stack trace
+ string entryType = DetermineLogTypeFromModeAndContent(mode, stackTrace);
+
+ // Skip if filtering and doesn't match
+ if (filter && !unityLogTypes.Contains(entryType))
+ continue;
+
+ // Create log object
+ var logObject = new JObject
+ {
+ ["message"] = actualMessage,
+ ["type"] = entryType,
+ ["timestamp"] = DateTime.Now.AddSeconds(-(currentCount - i)).ToString("yyyy-MM-dd HH:mm:ss.fff")
+ };
+
+ // Include stack trace if requested
+ if (includeStackTrace && !string.IsNullOrEmpty(stackTrace))
+ {
+ logObject["stackTrace"] = stackTrace;
+ }
+
+ collectedLogs.Add(logObject);
+ }
+
+ // End getting entries
+ _endGettingEntriesMethod?.Invoke(null, null);
+
+ // Apply pagination
+ var paginatedLogs = collectedLogs
+ .Skip(offset)
+ .Take(limit)
+ .ToList();
+
+ foreach (var log in paginatedLogs)
+ {
+ logsArray.Add(log);
+ }
+
+ return new JObject
+ {
+ ["logs"] = logsArray,
+ ["_totalCount"] = totalCount,
+ ["_filteredCount"] = collectedLogs.Count,
+ ["_returnedCount"] = paginatedLogs.Count,
+ ["message"] = $"Retrieved {paginatedLogs.Count} of {collectedLogs.Count} log entries (offset: {offset}, limit: {limit}, total: {totalCount})",
+ ["success"] = true
+ };
+ }
+ catch (Exception ex)
+ {
+ Debug.LogError($"[MCP Unity] Error getting logs: {ex.Message}");
+ return new JObject
+ {
+ ["logs"] = new JArray(),
+ ["message"] = $"Error retrieving logs: {ex.Message}",
+ ["success"] = false
+ };
+ }
+ }
+
+ private (string message, string stackTrace) ParseMessageAndStackTrace(string fullMessage, int callstackStartUTF16, int callstackStartUTF8)
+ {
+ if (string.IsNullOrEmpty(fullMessage))
+ return ("", "");
+
+ // Try UTF-16 position first (C# strings are UTF-16)
+ if (callstackStartUTF16 > 0 && callstackStartUTF16 < fullMessage.Length)
+ {
+ try
+ {
+ string message = fullMessage.Substring(0, callstackStartUTF16).TrimEnd('\n', '\r');
+ string stackTrace = fullMessage.Substring(callstackStartUTF16);
+ return (message, stackTrace);
+ }
+ catch
+ {
+ // Continue to next attempt
+ }
+ }
+
+ // Fallback to UTF-8 position
+ if (callstackStartUTF8 > 0 && callstackStartUTF8 < fullMessage.Length)
+ {
+ try
+ {
+ string message = fullMessage.Substring(0, callstackStartUTF8).TrimEnd('\n', '\r');
+ string stackTrace = fullMessage.Substring(callstackStartUTF8);
+ return (message, stackTrace);
+ }
+ catch
+ {
+ // Continue to heuristic parsing
+ }
+ }
+
+ // Fallback: heuristic parsing (previous method)
+ var lines = fullMessage.Split(new[] { '\n' }, StringSplitOptions.None);
+
+ if (lines.Length == 1)
+ {
+ return (fullMessage, "");
+ }
+
+ // Find the first line that looks like a stack trace
+ int stackTraceStartIndex = -1;
+ for (int i = 1; i < lines.Length; i++)
+ {
+ var line = lines[i];
+ if (line.StartsWith("UnityEngine.") ||
+ line.StartsWith("System.") ||
+ line.Contains(" (at ") ||
+ line.Contains(":") && (line.Contains("(") && line.Contains(")")))
+ {
+ stackTraceStartIndex = i;
+ break;
+ }
+ }
+
+ if (stackTraceStartIndex == -1)
+ {
+ return (fullMessage, "");
+ }
+
+ string fallbackMessage = string.Join("\n", lines.Take(stackTraceStartIndex));
+ string fallbackStackTrace = string.Join("\n", lines.Skip(stackTraceStartIndex));
+
+ return (fallbackMessage, fallbackStackTrace);
+ }
+
+ private string DetermineLogTypeFromModeAndContent(int mode, string stackTrace)
+ {
+ // First try to determine from stack trace content
+ if (!string.IsNullOrEmpty(stackTrace))
+ {
+ if (stackTrace.Contains("UnityEngine.Debug:LogError") ||
+ stackTrace.Contains("UnityEngine.Logger:LogError"))
+ return "Error";
+
+ if (stackTrace.Contains("UnityEngine.Debug:LogWarning") ||
+ stackTrace.Contains("UnityEngine.Logger:LogWarning"))
+ return "Warning";
+
+ if (stackTrace.Contains("UnityEngine.Debug:LogException") ||
+ stackTrace.Contains("UnityEngine.Logger:LogException"))
+ return "Exception";
+
+ if (stackTrace.Contains("UnityEngine.Debug:LogAssertion") ||
+ stackTrace.Contains("UnityEngine.Assertions.Assert"))
+ return "Assert";
+
+ if (stackTrace.Contains("UnityEngine.Debug:Log"))
+ return "Log";
+ }
+
+ // Fallback to mode flags
+ return GetLogTypeFromMode(mode);
+ }
+
+ private string GetLogTypeFromMode(int mode)
+ {
+ // Use centralized mode flags logic
+ return LogEntryModeFlags.GetLogTypeFromMode(mode);
+ }
+
+ public void CleanupOldLogs(int keepCount = 500)
+ {
+ // Not needed for Unity 6 implementation as we query directly from Unity
+ }
+
+ public int GetLogCount()
+ {
+ ConsoleWindowUtility.GetConsoleLogCounts(out int error, out int warning, out int log);
+ return error + warning + log;
+ }
+
+ ///
+ /// Search logs with keyword or regex pattern
+ ///
+ public JObject SearchLogsAsJson(string keyword = null, string regex = null, string logType = null,
+ bool includeStackTrace = true, bool caseSensitive = false, int offset = 0, int limit = 50)
+ {
+ // Prepare search criteria
+ bool hasSearchCriteria = !string.IsNullOrEmpty(keyword) || !string.IsNullOrEmpty(regex);
+ Regex searchRegex = null;
+ string searchKeyword = keyword;
+
+ // If regex is provided, use it instead of keyword
+ if (!string.IsNullOrEmpty(regex))
+ {
+ try
+ {
+ searchRegex = new Regex(regex, caseSensitive ? RegexOptions.None : RegexOptions.IgnoreCase);
+ }
+ catch (ArgumentException ex)
+ {
+ return new JObject
+ {
+ ["logs"] = new JArray(),
+ ["error"] = $"Invalid regex pattern: {ex.Message}",
+ ["success"] = false
+ };
+ }
+ }
+ else if (!string.IsNullOrEmpty(keyword) && !caseSensitive)
+ {
+ searchKeyword = keyword.ToLower();
+ }
+
+ // Map MCP log types to Unity log types
+ HashSet unityLogTypes = null;
+ if (!string.IsNullOrEmpty(logType))
+ {
+ if (LogTypeMapping.TryGetValue(logType, out var mapped))
+ {
+ unityLogTypes = mapped;
+ }
+ else
+ {
+ unityLogTypes = new HashSet(StringComparer.OrdinalIgnoreCase) { logType };
+ }
+ }
+
+ JArray logsArray = new JArray();
+ int totalCount = 0;
+ int filteredCount = 0;
+ int matchedCount = 0;
+ int currentIndex = 0;
+
+ // Get total count using reflection
+ try
+ {
+ totalCount = (int)_getCountMethod.Invoke(null, null);
+ }
+ catch (Exception ex)
+ {
+ Debug.LogError($"[MCP Unity] Error getting log count: {ex.Message}");
+ return new JObject
+ {
+ ["logs"] = logsArray,
+ ["error"] = "Failed to access Unity console logs",
+ ["success"] = false
+ };
+ }
+
+ if (totalCount == 0)
+ {
+ return new JObject
+ {
+ ["logs"] = logsArray,
+ ["_totalCount"] = 0,
+ ["_filteredCount"] = 0,
+ ["_matchedCount"] = 0,
+ ["_returnedCount"] = 0,
+ ["success"] = true
+ };
+ }
+
+ try
+ {
+ // Start getting entries
+ _startGettingEntriesMethod?.Invoke(null, null);
+
+ // Search through logs (newest first)
+ for (int i = totalCount - 1; i >= 0; i--)
+ {
+ // Create LogEntry instance
+ var logEntry = Activator.CreateInstance(_logEntryType);
+
+ // GetEntryInternal(int row, LogEntry outputEntry)
+ bool success = (bool)_getEntryInternalMethod.Invoke(null, new object[] { i, logEntry });
+
+ if (!success) continue;
+
+ // Extract fields
+ string fullMessage = _messageField?.GetValue(logEntry) as string ?? "";
+ string file = _fileField?.GetValue(logEntry) as string ?? "";
+ int line = _lineField?.GetValue(logEntry) as int? ?? 0;
+ int mode = _modeField?.GetValue(logEntry) as int? ?? 0;
+ int callstackStartUTF8 = _callstackTextStartUTF8Field?.GetValue(logEntry) as int? ?? 0;
+ int callstackStartUTF16 = _callstackTextStartUTF16Field?.GetValue(logEntry) as int? ?? 0;
+
+ // Parse message and stack trace
+ var (actualMessage, stackTrace) = ParseMessageAndStackTrace(fullMessage, callstackStartUTF16, callstackStartUTF8);
+
+ // Determine log type
+ string entryLogType = DetermineLogTypeFromModeAndContent(mode, stackTrace);
+
+ // Skip if filtering by log type and entry doesn't match
+ if (unityLogTypes != null && !unityLogTypes.Contains(entryLogType))
+ continue;
+
+ filteredCount++;
+
+ // Check if entry matches search criteria
+ bool matches = true;
+ if (hasSearchCriteria)
+ {
+ matches = false;
+
+ // Search in message
+ if (searchRegex != null)
+ {
+ matches = searchRegex.IsMatch(actualMessage);
+ if (!matches && includeStackTrace && !string.IsNullOrEmpty(stackTrace))
+ {
+ matches = searchRegex.IsMatch(stackTrace);
+ }
+ }
+ else if (!string.IsNullOrEmpty(searchKeyword))
+ {
+ string messageToSearch = caseSensitive ? actualMessage : actualMessage.ToLower();
+ matches = messageToSearch.Contains(searchKeyword);
+
+ if (!matches && includeStackTrace && !string.IsNullOrEmpty(stackTrace))
+ {
+ string stackTraceToSearch = caseSensitive ? stackTrace : stackTrace.ToLower();
+ matches = stackTraceToSearch.Contains(searchKeyword);
+ }
+ }
+ }
+
+ if (!matches) continue;
+
+ matchedCount++;
+
+ // Check if we're in the offset range and haven't reached the limit yet
+ if (currentIndex >= offset && logsArray.Count < limit)
+ {
+ var logObject = new JObject
+ {
+ ["message"] = actualMessage,
+ ["type"] = entryLogType,
+ ["timestamp"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")
+ };
+
+ // Only include stack trace if requested
+ if (includeStackTrace)
+ {
+ logObject["stackTrace"] = stackTrace;
+ }
+
+ logsArray.Add(logObject);
+ }
+
+ currentIndex++;
+
+ // Early exit if we've collected enough logs
+ if (currentIndex >= offset + limit) break;
+ }
+ }
+ finally
+ {
+ // End getting entries
+ _endGettingEntriesMethod?.Invoke(null, null);
+ }
+
+ return new JObject
+ {
+ ["logs"] = logsArray,
+ ["_totalCount"] = totalCount,
+ ["_filteredCount"] = filteredCount,
+ ["_matchedCount"] = matchedCount,
+ ["_returnedCount"] = logsArray.Count,
+ ["success"] = true
+ };
+ }
+
+ #if MCP_UNITY_DEBUG_MODE_VALUES
+ ///
+ /// Debug method to write mode values to file for analysis
+ /// Enable by adding MCP_UNITY_DEBUG_MODE_VALUES to Project Settings > Player > Scripting Define Symbols
+ ///
+ private void WriteDebugInfo(string message, int mode)
+ {
+ try
+ {
+ string debugPath = Path.Combine(Application.dataPath, "..", "mcp-unity-debug.log");
+ string debugLine = $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} | Mode: {mode} (0x{mode:X}) Binary: {Convert.ToString(mode, 2)} | Message: {message.Substring(0, Math.Min(100, message.Length))}...\n";
+ File.AppendAllText(debugPath, debugLine);
+ }
+ catch
+ {
+ // Ignore debug write errors
+ }
+ }
+ #endif
+ }
+}
\ No newline at end of file
diff --git a/Editor/Services/ConsoleLogsServiceUnity6.cs.meta b/Editor/Services/ConsoleLogsServiceUnity6.cs.meta
new file mode 100644
index 00000000..a9326b0d
--- /dev/null
+++ b/Editor/Services/ConsoleLogsServiceUnity6.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 4534e9a288ddd411891ebe758d3bb332
\ No newline at end of file
diff --git a/Editor/Services/IConsoleLogsService.cs b/Editor/Services/IConsoleLogsService.cs
index 23ce313b..a0f2de99 100644
--- a/Editor/Services/IConsoleLogsService.cs
+++ b/Editor/Services/IConsoleLogsService.cs
@@ -41,5 +41,19 @@ public interface IConsoleLogsService
///
/// Number of stored log entries
int GetLogCount();
+
+ ///
+ /// Search logs with keyword or regex pattern
+ ///
+ /// Keyword to search for (partial match)
+ /// Regular expression pattern (overrides keyword if provided)
+ /// Filter by log type (empty for all)
+ /// Whether to include stack trace in search (default: true)
+ /// Whether the search is case sensitive (default: false)
+ /// Starting index (0-based)
+ /// Maximum number of logs to return (default: 50)
+ /// JObject containing matching logs array and pagination info
+ JObject SearchLogsAsJson(string keyword = null, string regex = null, string logType = null,
+ bool includeStackTrace = true, bool caseSensitive = false, int offset = 0, int limit = 50);
}
}
diff --git a/Editor/Services/LogEntryModeFlags.cs b/Editor/Services/LogEntryModeFlags.cs
new file mode 100644
index 00000000..ea3fc1d1
--- /dev/null
+++ b/Editor/Services/LogEntryModeFlags.cs
@@ -0,0 +1,60 @@
+namespace McpUnity.Services
+{
+ ///
+ /// Unity LogEntry mode flags constants
+ /// Based on Unity's internal LogMessageFlags from UnityCsReference
+ /// https://github.com/Unity-Technologies/UnityCsReference/blob/master/Editor/Mono/LogEntries.bindings.cs
+ ///
+ public static class LogEntryModeFlags
+ {
+ // Basic log type flags (bits 0-4)
+ public const int kModeError = 1 << 0; // 1 (0x1)
+ public const int kModeAssert = 1 << 1; // 2 (0x2)
+ public const int kModeLog = 1 << 2; // 4 (0x4)
+ public const int kModeWarning = 1 << 3; // 8 (0x8)
+ public const int kModeException = 1 << 4; // 16 (0x10)
+
+ // Scripting related flags (bits 8-12)
+ public const int kScriptingError = 1 << 8; // 256 (0x100)
+ public const int kScriptingWarning = 1 << 9; // 512 (0x200)
+ public const int kScriptingLog = 1 << 10; // 1024 (0x400)
+ public const int kScriptCompileError = 1 << 11; // 2048 (0x800)
+ public const int kScriptCompileWarning = 1 << 12; // 4096 (0x1000)
+
+ // Observed composite values from debugging
+ // These include additional undocumented high bits
+ public const int ObservedCompilerWarning = 266240; // 0x41000 (bits: 18, 10)
+ public const int ObservedCompilerError = 272384; // 0x42800 (bits: 18, 14, 10)
+ public const int ObservedShaderError = 262212; // 0x40044 (bits: 18, 6, 2)
+ public const int ObservedRuntimeWarning = 8405504; // 0x804200 (bits: 23, 18, 9)
+ public const int ObservedRuntimeError = 8405248; // 0x804100 (bits: 23, 18, 8)
+
+ ///
+ /// Determine log type from mode flags
+ ///
+ public static string GetLogTypeFromMode(int mode)
+ {
+ // Check for observed compiler/shader message patterns first
+ if (mode == ObservedCompilerError) return "Error";
+ if (mode == ObservedCompilerWarning) return "Warning";
+ if (mode == ObservedShaderError) return "Error";
+
+ // Check for script compile errors/warnings
+ if ((mode & kScriptCompileError) != 0) return "Error";
+ if ((mode & kScriptCompileWarning) != 0) return "Warning";
+
+ // Check for scripting errors/warnings
+ if ((mode & kScriptingError) != 0) return "Error";
+ if ((mode & kScriptingWarning) != 0) return "Warning";
+
+ // Then check standard flags
+ if ((mode & kModeError) != 0) return "Error";
+ if ((mode & kModeAssert) != 0) return "Assert";
+ if ((mode & kModeException) != 0) return "Exception";
+ if ((mode & kModeWarning) != 0) return "Warning";
+ if ((mode & kModeLog) != 0) return "Log";
+
+ return "Log"; // Default to Log instead of Unknown
+ }
+ }
+}
\ No newline at end of file
diff --git a/Editor/Services/LogEntryModeFlags.cs.meta b/Editor/Services/LogEntryModeFlags.cs.meta
new file mode 100644
index 00000000..62d5b163
--- /dev/null
+++ b/Editor/Services/LogEntryModeFlags.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: a2b3c4d5e6f7890123456789abcdef01
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
\ No newline at end of file
diff --git a/Editor/Services/Unity6InternalAPIReference.md b/Editor/Services/Unity6InternalAPIReference.md
new file mode 100644
index 00000000..2fa665de
--- /dev/null
+++ b/Editor/Services/Unity6InternalAPIReference.md
@@ -0,0 +1,84 @@
+# Unity 6 Internal API Reference
+
+## LogEntry Structure
+
+Unity's internal `LogEntry` structure contains the following fields:
+
+### Fields
+| Field Name | Type | Description |
+|------------|------|-------------|
+| message | String | Full message including stack trace |
+| file | String | Source file path |
+| line | Int32 | Line number |
+| column | Int32 | Column number (-1 if not available) |
+| mode | Int32 | Log type flags (see Mode Flags section) |
+| instanceID | Int32 | Instance ID of the object |
+| identifier | Int32 | Unique identifier |
+| globalLineIndex | Int32 | Global line index in console |
+| callstackTextStartUTF8 | Int32 | UTF-8 byte position where stack trace starts |
+| callstackTextStartUTF16 | Int32 | UTF-16 character position where stack trace starts |
+
+## Mode Flags
+
+### Standard Unity Log Flags (from UnityCsReference)
+```csharp
+// Basic log types
+const int kModeError = 1 << 0; // 1 (0x1)
+const int kModeAssert = 1 << 1; // 2 (0x2)
+const int kModeLog = 1 << 2; // 4 (0x4)
+const int kModeWarning = 1 << 3; // 8 (0x8)
+const int kModeException = 1 << 4; // 16 (0x10)
+
+// Scripting related flags
+const int kScriptingError = 1 << 8; // 256 (0x100)
+const int kScriptingWarning = 1 << 9; // 512 (0x200)
+const int kScriptingLog = 1 << 10; // 1024 (0x400)
+const int kScriptCompileError = 1 << 11; // 2048 (0x800)
+const int kScriptCompileWarning = 1 << 12; // 4096 (0x1000)
+```
+
+### Observed Compiler/Shader Message Values (from debugging)
+```csharp
+// These are composite values with multiple flags set
+const int ObservedCompilerWarning = 266240; // 0x41000 (bits: 18, 10)
+const int ObservedCompilerError = 272384; // 0x42800 (bits: 18, 14, 10)
+const int ObservedShaderError = 262212; // 0x40044 (bits: 18, 6, 2)
+```
+
+Note: The observed values include additional high bits (2, 6, 10, 14, 18) beyond the documented flags, suggesting Unity may be using undocumented internal flags.
+
+## Important Findings
+
+1. **Compiler messages use special mode values**
+ - C# compilation errors (e.g., `error CS0103`) have mode = 272384
+ - C# compilation warnings (e.g., `warning CS0414`) have mode = 266240
+ - These do NOT use the standard error/warning flags!
+
+2. **Shader errors**
+ - Shader compilation errors appear to use standard Log mode (4)
+ - Need message content analysis to properly classify
+
+3. **Stack trace separation**
+ - Use `callstackTextStartUTF16` for C# strings (preferred)
+ - Fallback to `callstackTextStartUTF8` if needed
+ - Unity provides exact position where stack trace begins
+
+## Usage Notes
+
+- Always check special compiler flags before standard flags
+- Message content analysis may still be needed for some error types
+- The mode field alone is not sufficient for all classification needs
+
+## Example Mode Analysis
+
+```
+Warning CS0414: mode = 266240 = 0x41000
+Binary: 1000001000000000000
+Bits set: 18, 10
+
+Error CS0103: mode = 272384 = 0x42800
+Binary: 1000010100000000000
+Bits set: 18, 14, 10
+```
+
+The high bits (10, 14, 18) appear to indicate compiler-related messages.
\ No newline at end of file
diff --git a/Editor/Services/Unity6InternalAPIReference.md.meta b/Editor/Services/Unity6InternalAPIReference.md.meta
new file mode 100644
index 00000000..2b7e18e3
--- /dev/null
+++ b/Editor/Services/Unity6InternalAPIReference.md.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 9f8e7d6c5b4a3214fa9b8c7d6e5f4321
+TextScriptImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
\ No newline at end of file
diff --git a/Editor/Tools/SearchConsoleLogsTool.cs b/Editor/Tools/SearchConsoleLogsTool.cs
new file mode 100644
index 00000000..82d93060
--- /dev/null
+++ b/Editor/Tools/SearchConsoleLogsTool.cs
@@ -0,0 +1,96 @@
+using System;
+using Newtonsoft.Json.Linq;
+using McpUnity.Services;
+
+namespace McpUnity.Tools
+{
+ ///
+ /// Tool for searching Unity console logs with keyword or regex pattern
+ ///
+ public class SearchConsoleLogsTool : McpToolBase
+ {
+ private readonly IConsoleLogsService _consoleLogsService;
+
+ public SearchConsoleLogsTool(IConsoleLogsService consoleLogsService)
+ {
+ Name = "search_console_logs";
+ Description = "Search Unity console logs with keyword or regex pattern. Supports case-sensitive/insensitive search, optional stack trace inclusion, and pagination. Regex takes precedence over keyword if both are provided.";
+
+ _consoleLogsService = consoleLogsService;
+ }
+
+ ///
+ /// Execute the search console logs tool
+ ///
+ /// Tool parameters
+ /// JObject containing search results
+ public override JObject Execute(JObject parameters)
+ {
+ // Extract search parameters
+ string keyword = parameters?["keyword"]?.ToString();
+ string regex = parameters?["regex"]?.ToString();
+ string logType = parameters?["logType"]?.ToString();
+
+ bool includeStackTrace = GetBoolParameter(parameters, "includeStackTrace", true);
+ bool caseSensitive = GetBoolParameter(parameters, "caseSensitive", false);
+ int offset = Math.Max(0, GetIntParameter(parameters, "offset", 0));
+ int limit = Math.Max(1, Math.Min(1000, GetIntParameter(parameters, "limit", 50)));
+
+ // At least one search criteria must be provided
+ if (string.IsNullOrEmpty(keyword) && string.IsNullOrEmpty(regex))
+ {
+ return new JObject
+ {
+ ["logs"] = new JArray(),
+ ["error"] = "Either 'keyword' or 'regex' parameter must be provided",
+ ["success"] = false
+ };
+ }
+
+ // Call the search method
+ JObject result = _consoleLogsService.SearchLogsAsJson(
+ keyword, regex, logType, includeStackTrace, caseSensitive, offset, limit);
+
+ // Add formatted message if search was successful
+ if (result["success"]?.Value() == true)
+ {
+ string searchTerm = !string.IsNullOrEmpty(regex) ? $"regex '{regex}'" : $"keyword '{keyword}'";
+ string typeFilter = !string.IsNullOrEmpty(logType) ? $" of type '{logType}'" : "";
+ int returnedCount = result["_returnedCount"]?.Value() ?? 0;
+ int matchedCount = result["_matchedCount"]?.Value() ?? 0;
+ int filteredCount = result["_filteredCount"]?.Value() ?? 0;
+ int totalCount = result["_totalCount"]?.Value() ?? 0;
+
+ result["message"] = $"Found {matchedCount} logs matching {searchTerm}{typeFilter} (returned: {returnedCount}, filtered by type: {filteredCount}, total: {totalCount})";
+
+ // Remove internal count fields (they're now in the message)
+ result.Remove("_totalCount");
+ result.Remove("_filteredCount");
+ result.Remove("_matchedCount");
+ result.Remove("_returnedCount");
+ }
+
+ return result;
+ }
+
+ ///
+ /// Helper method to safely extract integer parameters with default values
+ ///
+ 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;
+ }
+
+ ///
+ /// Helper method to safely extract boolean parameters with default values
+ ///
+ 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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Editor/Tools/SearchConsoleLogsTool.cs.meta b/Editor/Tools/SearchConsoleLogsTool.cs.meta
new file mode 100644
index 00000000..4f8dfbb0
--- /dev/null
+++ b/Editor/Tools/SearchConsoleLogsTool.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: b51f73e4710a04aafa4146a0deb9f77a
\ No newline at end of file
diff --git a/Editor/UnityBridge/McpUnityEditorWindow.cs b/Editor/UnityBridge/McpUnityEditorWindow.cs
index 4ddcf463..02c6579e 100644
--- a/Editor/UnityBridge/McpUnityEditorWindow.cs
+++ b/Editor/UnityBridge/McpUnityEditorWindow.cs
@@ -141,6 +141,30 @@ private void DrawServerTab()
settings.SaveSettings();
}
+ EditorGUILayout.Space();
+
+ // Console log service selection
+ ConsoleLogServiceType newConsoleLogService = (ConsoleLogServiceType)EditorGUILayout.EnumPopup(
+ new GUIContent("Console Log Service", "Select console log service implementation. EventBased is safe but may miss some logs. Unity6Enhanced uses internal APIs for better reliability but requires Unity 6+. Server restart required when changed."),
+ settings.ConsoleLogService);
+ if (newConsoleLogService != settings.ConsoleLogService)
+ {
+ settings.ConsoleLogService = newConsoleLogService;
+ settings.SaveSettings();
+
+ // Show warning for Unity6Enhanced
+ if (newConsoleLogService == ConsoleLogServiceType.Unity6Enhanced)
+ {
+#if UNITY_6000_0_OR_NEWER
+ EditorUtility.DisplayDialog("Console Log Service Changed",
+ "Unity 6 enhanced console log service selected. This uses internal Unity APIs for better reliability but may break in future Unity versions.\n\nRestart the MCP server for changes to take effect.", "OK");
+#else
+ EditorUtility.DisplayDialog("Console Log Service Warning",
+ $"Unity 6 enhanced console log service selected but current Unity version is {Application.unityVersion}. The service will fall back to event-based implementation.\n\nRestart the MCP server for changes to take effect.", "OK");
+#endif
+ }
+ }
+
EditorGUILayout.Space();
// Server control buttons
diff --git a/Editor/UnityBridge/McpUnityServer.cs b/Editor/UnityBridge/McpUnityServer.cs
index dfabf2be..3eccafc0 100644
--- a/Editor/UnityBridge/McpUnityServer.cs
+++ b/Editor/UnityBridge/McpUnityServer.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using System.Threading;
using UnityEngine;
using UnityEditor;
@@ -28,7 +29,7 @@ public class McpUnityServer
private WebSocketServer _webSocketServer;
private CancellationTokenSource _cts;
private TestRunnerService _testRunnerService;
- private ConsoleLogsService _consoleLogsService;
+ private IConsoleLogsService _consoleLogsService;
///
/// Static constructor that gets called when Unity loads due to InitializeOnLoad attribute
@@ -141,6 +142,14 @@ public bool TryGetResource(string name, out McpResourceBase resource)
{
return _resources.TryGetValue(name, out resource);
}
+
+ ///
+ /// Get all resource names for debugging
+ ///
+ public string[] GetResourceNames()
+ {
+ return _resources.Keys.ToArray();
+ }
///
/// Installs the MCP Node.js server by running 'npm install' and 'npm run build'
@@ -205,6 +214,10 @@ private void RegisterTools()
// Register AddAssetToSceneTool
AddAssetToSceneTool addAssetToSceneTool = new AddAssetToSceneTool();
_tools.Add(addAssetToSceneTool.Name, addAssetToSceneTool);
+
+ // Register SearchConsoleLogsTool
+ SearchConsoleLogsTool searchConsoleLogsTool = new SearchConsoleLogsTool(_consoleLogsService);
+ _tools.Add(searchConsoleLogsTool.Name, searchConsoleLogsTool);
}
///
@@ -249,8 +262,26 @@ private void InitializeServices()
// Initialize the test runner service
_testRunnerService = new TestRunnerService();
- // Initialize the console logs service
- _consoleLogsService = new ConsoleLogsService();
+ // Initialize the console logs service based on settings and Unity version
+ // Default to safe event-based implementation
+ var consoleLogService = McpUnitySettings.Instance.ConsoleLogService;
+
+ if (consoleLogService == ConsoleLogServiceType.Unity6Enhanced)
+ {
+#if UNITY_6000_0_OR_NEWER
+ _consoleLogsService = new ConsoleLogsServiceUnity6();
+ McpLogger.LogInfo($"[MCP Unity] Console Log Service: Unity 6 Enhanced (experimental, uses internal APIs)");
+#else
+ McpLogger.LogWarning($"[MCP Unity] Console Log Service: Unity 6 Enhanced requested but Unity version is {Application.unityVersion}. Falling back to Event-Based service.");
+ _consoleLogsService = new ConsoleLogsService();
+#endif
+ }
+ else
+ {
+ // Default to safe event-based implementation
+ _consoleLogsService = new ConsoleLogsService();
+ McpLogger.LogInfo($"[MCP Unity] Console Log Service: Event-Based (default, safe)");
+ }
}
}
}
diff --git a/Editor/UnityBridge/McpUnitySettings.cs b/Editor/UnityBridge/McpUnitySettings.cs
index 47dd6624..c9aac1db 100644
--- a/Editor/UnityBridge/McpUnitySettings.cs
+++ b/Editor/UnityBridge/McpUnitySettings.cs
@@ -6,6 +6,18 @@
namespace McpUnity.Unity
{
+ ///
+ /// Console log service implementation options
+ ///
+ public enum ConsoleLogServiceType
+ {
+ [Tooltip("Event-based implementation (default, safe) - may occasionally miss logs")]
+ EventBased,
+
+ [Tooltip("Unity 6 enhanced implementation (experimental) - uses internal APIs for better reliability")]
+ Unity6Enhanced
+ }
+
///
/// Handles persistence of MCP Unity settings
///
@@ -36,6 +48,9 @@ public class McpUnitySettings
[Tooltip("Optional: Full path to the npm executable (e.g., /Users/user/.asdf/shims/npm or C:\\path\\to\\npm.cmd). If not set, 'npm' from the system PATH will be used.")]
public string NpmExecutablePath = string.Empty;
+
+ [Tooltip("Console log service implementation to use. EventBased (default, safe) may occasionally miss logs. Unity6Enhanced uses internal APIs for better reliability but requires Unity 6+ and should only be used when explicitly needed. Server restart required when changed.")]
+ public ConsoleLogServiceType ConsoleLogService = ConsoleLogServiceType.EventBased;
///
/// Singleton instance of settings
diff --git a/Editor/UnityBridge/McpUnitySocketHandler.cs b/Editor/UnityBridge/McpUnitySocketHandler.cs
index 63c429f0..b5bcd944 100644
--- a/Editor/UnityBridge/McpUnitySocketHandler.cs
+++ b/Editor/UnityBridge/McpUnitySocketHandler.cs
@@ -74,10 +74,12 @@ protected override async void OnMessage(MessageEventArgs e)
}
else if (_server.TryGetResource(method, out var resource))
{
+ McpLogger.LogInfo($"[DEBUG] Found resource: {resource.Name} for method: {method}");
EditorCoroutineUtility.StartCoroutineOwnerless(FetchResourceCoroutine(resource, parameters, tcs));
}
else
{
+ McpLogger.LogWarning($"[DEBUG] Unknown method: {method}. Available resources: {string.Join(", ", _server.GetResourceNames())}");
tcs.SetResult(CreateErrorResponse($"Unknown method: {method}", "unknown_method"));
}
diff --git a/Server~/build/index.js b/Server~/build/index.js
index cf0cc708..35baa814 100644
--- a/Server~/build/index.js
+++ b/Server~/build/index.js
@@ -20,6 +20,7 @@ import { registerGetAssetsResource } from './resources/getAssetsResource.js';
import { registerGetTestsResource } from './resources/getTestsResource.js';
import { registerGetGameObjectResource } from './resources/getGameObjectResource.js';
import { registerGameObjectHandlingPrompt } from './prompts/gameobjectHandlingPrompt.js';
+import { registerSearchConsoleLogsTool } from './tools/searchConsoleLogsTool.js';
// Initialize loggers
const serverLogger = new Logger('Server', LogLevel.INFO);
const unityLogger = new Logger('Unity', LogLevel.INFO);
@@ -48,6 +49,7 @@ registerGetConsoleLogsTool(server, mcpUnity, toolLogger);
registerUpdateComponentTool(server, mcpUnity, toolLogger);
registerAddAssetToSceneTool(server, mcpUnity, toolLogger);
registerUpdateGameObjectTool(server, mcpUnity, toolLogger);
+registerSearchConsoleLogsTool(server, mcpUnity, toolLogger);
// Register all resources into the MCP server
registerGetTestsResource(server, mcpUnity, resourceLogger);
registerGetGameObjectResource(server, mcpUnity, resourceLogger);
diff --git a/Server~/build/utils/errors.js b/Server~/build/utils/errors.js
index 7382c65f..429a4d72 100644
--- a/Server~/build/utils/errors.js
+++ b/Server~/build/utils/errors.js
@@ -6,7 +6,7 @@ export var ErrorType;
ErrorType["VALIDATION"] = "validation_error";
ErrorType["INTERNAL"] = "internal_error";
ErrorType["TIMEOUT"] = "timeout_error";
-})(ErrorType || (ErrorType = {}));
+})(ErrorType = ErrorType || (ErrorType = {}));
export class McpUnityError extends Error {
type;
details;
diff --git a/Server~/build/utils/logger.js b/Server~/build/utils/logger.js
index ec6b4c13..212c2829 100644
--- a/Server~/build/utils/logger.js
+++ b/Server~/build/utils/logger.js
@@ -5,7 +5,7 @@ export var LogLevel;
LogLevel[LogLevel["INFO"] = 1] = "INFO";
LogLevel[LogLevel["WARN"] = 2] = "WARN";
LogLevel[LogLevel["ERROR"] = 3] = "ERROR";
-})(LogLevel || (LogLevel = {}));
+})(LogLevel = LogLevel || (LogLevel = {}));
// Check environment variable for logging
const isLoggingEnabled = process.env.LOGGING === 'true';
// Check environment variable for logging in a file
diff --git a/Server~/src/index.ts b/Server~/src/index.ts
index d8d67e10..40714cce 100644
--- a/Server~/src/index.ts
+++ b/Server~/src/index.ts
@@ -20,6 +20,7 @@ import { registerGetAssetsResource } from './resources/getAssetsResource.js';
import { registerGetTestsResource } from './resources/getTestsResource.js';
import { registerGetGameObjectResource } from './resources/getGameObjectResource.js';
import { registerGameObjectHandlingPrompt } from './prompts/gameobjectHandlingPrompt.js';
+import { registerSearchConsoleLogsTool } from './tools/searchConsoleLogsTool.js';
// Initialize loggers
const serverLogger = new Logger('Server', LogLevel.INFO);
@@ -55,6 +56,7 @@ registerGetConsoleLogsTool(server, mcpUnity, toolLogger);
registerUpdateComponentTool(server, mcpUnity, toolLogger);
registerAddAssetToSceneTool(server, mcpUnity, toolLogger);
registerUpdateGameObjectTool(server, mcpUnity, toolLogger);
+registerSearchConsoleLogsTool(server, mcpUnity, toolLogger);
// Register all resources into the MCP server
registerGetTestsResource(server, mcpUnity, resourceLogger);