diff --git a/.gitignore b/.gitignore
index fd0d720..ce4b2f3 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/ConsoleLogsServiceUnity6.cs b/Editor/Services/ConsoleLogsServiceUnity6.cs
new file mode 100644
index 0000000..0a7acd3
--- /dev/null
+++ b/Editor/Services/ConsoleLogsServiceUnity6.cs
@@ -0,0 +1,385 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+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;
+ }
+
+ #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 0000000..a9326b0
--- /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/LogEntryModeFlags.cs b/Editor/Services/LogEntryModeFlags.cs
new file mode 100644
index 0000000..ea3fc1d
--- /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 0000000..62d5b16
--- /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 0000000..2fa665d
--- /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 0000000..2b7e18e
--- /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/UnityBridge/McpUnityEditorWindow.cs b/Editor/UnityBridge/McpUnityEditorWindow.cs
index 70c1eed..1a94cbd 100644
--- a/Editor/UnityBridge/McpUnityEditorWindow.cs
+++ b/Editor/UnityBridge/McpUnityEditorWindow.cs
@@ -143,6 +143,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 dfabf2b..b059500 100644
--- a/Editor/UnityBridge/McpUnityServer.cs
+++ b/Editor/UnityBridge/McpUnityServer.cs
@@ -28,7 +28,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
@@ -249,8 +249,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 1720fa1..676a657 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