added read_console tool
parent
be47f39b12
commit
73ea045c20
|
|
@ -2,6 +2,11 @@ using UnityEngine;
|
||||||
using UnityEditor;
|
using UnityEditor;
|
||||||
using UnityEditor.Build.Reporting;
|
using UnityEditor.Build.Reporting;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq; // Add LINQ namespace for Select extension method
|
||||||
|
using System.Globalization;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handles editor control commands like undo, redo, play, pause, stop, and build operations.
|
/// Handles editor control commands like undo, redo, play, pause, stop, and build operations.
|
||||||
|
|
@ -32,6 +37,8 @@ public static class EditorControlHandler
|
||||||
return HandleBuild(commandParams);
|
return HandleBuild(commandParams);
|
||||||
case "EXECUTE_COMMAND":
|
case "EXECUTE_COMMAND":
|
||||||
return HandleExecuteCommand(commandParams);
|
return HandleExecuteCommand(commandParams);
|
||||||
|
case "READ_CONSOLE":
|
||||||
|
return ReadConsole(commandParams);
|
||||||
default:
|
default:
|
||||||
return new { error = $"Unknown editor control command: {command}" };
|
return new { error = $"Unknown editor control command: {command}" };
|
||||||
}
|
}
|
||||||
|
|
@ -124,6 +131,303 @@ public static class EditorControlHandler
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads log messages from the Unity Console
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="params">Parameters containing filtering options</param>
|
||||||
|
/// <returns>Object containing console messages filtered by type</returns>
|
||||||
|
public static object ReadConsole(JObject @params)
|
||||||
|
{
|
||||||
|
// Default values for show flags
|
||||||
|
bool showLogs = true;
|
||||||
|
bool showWarnings = true;
|
||||||
|
bool showErrors = true;
|
||||||
|
string searchTerm = string.Empty;
|
||||||
|
|
||||||
|
// Get filter parameters if provided
|
||||||
|
if (@params != null)
|
||||||
|
{
|
||||||
|
if (@params["show_logs"] != null) showLogs = (bool)@params["show_logs"];
|
||||||
|
if (@params["show_warnings"] != null) showWarnings = (bool)@params["show_warnings"];
|
||||||
|
if (@params["show_errors"] != null) showErrors = (bool)@params["show_errors"];
|
||||||
|
if (@params["search_term"] != null) searchTerm = (string)@params["search_term"];
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Get required types and methods via reflection
|
||||||
|
Type logEntriesType = Type.GetType("UnityEditor.LogEntries,UnityEditor");
|
||||||
|
Type logEntryType = Type.GetType("UnityEditor.LogEntry,UnityEditor");
|
||||||
|
|
||||||
|
if (logEntriesType == null || logEntryType == null)
|
||||||
|
return new { error = "Could not find required Unity logging types", entries = new List<object>() };
|
||||||
|
|
||||||
|
// Get essential methods
|
||||||
|
MethodInfo getCountMethod = logEntriesType.GetMethod("GetCount", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
|
||||||
|
MethodInfo getEntryMethod = logEntriesType.GetMethod("GetEntryAt", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) ??
|
||||||
|
logEntriesType.GetMethod("GetEntryInternal", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
|
||||||
|
|
||||||
|
if (getCountMethod == null || getEntryMethod == null)
|
||||||
|
return new { error = "Could not find required Unity logging methods", entries = new List<object>() };
|
||||||
|
|
||||||
|
// Get stack trace method if available
|
||||||
|
MethodInfo getStackTraceMethod = logEntriesType.GetMethod("GetEntryStackTrace", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic,
|
||||||
|
null, new[] { typeof(int) }, null) ?? logEntriesType.GetMethod("GetStackTrace", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic,
|
||||||
|
null, new[] { typeof(int) }, null);
|
||||||
|
|
||||||
|
// Get entry count and prepare result list
|
||||||
|
int count = (int)getCountMethod.Invoke(null, null);
|
||||||
|
var entries = new List<object>();
|
||||||
|
|
||||||
|
// Create LogEntry instance to populate
|
||||||
|
object logEntryInstance = Activator.CreateInstance(logEntryType);
|
||||||
|
|
||||||
|
// Find properties on LogEntry type
|
||||||
|
PropertyInfo modeProperty = logEntryType.GetProperty("mode") ?? logEntryType.GetProperty("Mode");
|
||||||
|
PropertyInfo messageProperty = logEntryType.GetProperty("message") ?? logEntryType.GetProperty("Message");
|
||||||
|
|
||||||
|
// Parse search terms if provided
|
||||||
|
string[] searchWords = !string.IsNullOrWhiteSpace(searchTerm) ?
|
||||||
|
searchTerm.ToLower().Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries) : null;
|
||||||
|
|
||||||
|
// Process each log entry
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Get log entry at index i
|
||||||
|
var methodParams = getEntryMethod.GetParameters();
|
||||||
|
if (methodParams.Length == 2 && methodParams[1].ParameterType == logEntryType)
|
||||||
|
{
|
||||||
|
getEntryMethod.Invoke(null, new object[] { i, logEntryInstance });
|
||||||
|
}
|
||||||
|
else if (methodParams.Length >= 1 && methodParams[0].ParameterType == typeof(int))
|
||||||
|
{
|
||||||
|
var parameters = new object[methodParams.Length];
|
||||||
|
parameters[0] = i;
|
||||||
|
for (int p = 1; p < parameters.Length; p++)
|
||||||
|
{
|
||||||
|
parameters[p] = methodParams[p].ParameterType.IsValueType ?
|
||||||
|
Activator.CreateInstance(methodParams[p].ParameterType) : null;
|
||||||
|
}
|
||||||
|
getEntryMethod.Invoke(null, parameters);
|
||||||
|
}
|
||||||
|
else continue;
|
||||||
|
|
||||||
|
// Extract log data
|
||||||
|
int logType = modeProperty != null ?
|
||||||
|
Convert.ToInt32(modeProperty.GetValue(logEntryInstance) ?? 0) : 0;
|
||||||
|
|
||||||
|
string message = messageProperty != null ?
|
||||||
|
(messageProperty.GetValue(logEntryInstance)?.ToString() ?? "") : "";
|
||||||
|
|
||||||
|
// If message is empty, try to get it via a field
|
||||||
|
if (string.IsNullOrEmpty(message))
|
||||||
|
{
|
||||||
|
var msgField = logEntryType.GetField("message", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
|
||||||
|
if (msgField != null)
|
||||||
|
{
|
||||||
|
object msgValue = msgField.GetValue(logEntryInstance);
|
||||||
|
message = msgValue != null ? msgValue.ToString() : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// If still empty, try alternate approach with Console window
|
||||||
|
if (string.IsNullOrEmpty(message))
|
||||||
|
{
|
||||||
|
// Access ConsoleWindow and its data
|
||||||
|
Type consoleWindowType = Type.GetType("UnityEditor.ConsoleWindow,UnityEditor");
|
||||||
|
if (consoleWindowType != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Get Console window instance
|
||||||
|
var getWindowMethod = consoleWindowType.GetMethod("GetWindow",
|
||||||
|
BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic,
|
||||||
|
null, new[] { typeof(bool) }, null) ??
|
||||||
|
consoleWindowType.GetMethod("GetConsoleWindow",
|
||||||
|
BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
|
||||||
|
|
||||||
|
if (getWindowMethod != null)
|
||||||
|
{
|
||||||
|
object consoleWindow = getWindowMethod.Invoke(null,
|
||||||
|
getWindowMethod.GetParameters().Length > 0 ? new object[] { false } : null);
|
||||||
|
|
||||||
|
if (consoleWindow != null)
|
||||||
|
{
|
||||||
|
// Try to find log entries collection
|
||||||
|
foreach (var prop in consoleWindowType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
|
||||||
|
{
|
||||||
|
if (prop.PropertyType.IsArray ||
|
||||||
|
(prop.PropertyType.IsGenericType && prop.PropertyType.GetGenericTypeDefinition() == typeof(List<>)))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var logItems = prop.GetValue(consoleWindow);
|
||||||
|
if (logItems != null)
|
||||||
|
{
|
||||||
|
if (logItems.GetType().IsArray && i < ((Array)logItems).Length)
|
||||||
|
{
|
||||||
|
var entry = ((Array)logItems).GetValue(i);
|
||||||
|
if (entry != null)
|
||||||
|
{
|
||||||
|
var entryType = entry.GetType();
|
||||||
|
var entryMessageProp = entryType.GetProperty("message") ??
|
||||||
|
entryType.GetProperty("Message");
|
||||||
|
if (entryMessageProp != null)
|
||||||
|
{
|
||||||
|
object value = entryMessageProp.GetValue(entry);
|
||||||
|
if (value != null)
|
||||||
|
{
|
||||||
|
message = value.ToString();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Ignore errors in this fallback approach
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Ignore errors in this fallback approach
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If still empty, try one more approach with log files
|
||||||
|
if (string.IsNullOrEmpty(message))
|
||||||
|
{
|
||||||
|
// This is our last resort - try to get log messages from the most recent Unity log file
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string logPath = string.Empty;
|
||||||
|
|
||||||
|
// Determine the log file path based on the platform
|
||||||
|
if (Application.platform == RuntimePlatform.WindowsEditor)
|
||||||
|
{
|
||||||
|
logPath = System.IO.Path.Combine(
|
||||||
|
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
|
||||||
|
"Unity", "Editor", "Editor.log");
|
||||||
|
}
|
||||||
|
else if (Application.platform == RuntimePlatform.OSXEditor)
|
||||||
|
{
|
||||||
|
logPath = System.IO.Path.Combine(
|
||||||
|
Environment.GetFolderPath(Environment.SpecialFolder.Personal),
|
||||||
|
"Library", "Logs", "Unity", "Editor.log");
|
||||||
|
}
|
||||||
|
else if (Application.platform == RuntimePlatform.LinuxEditor)
|
||||||
|
{
|
||||||
|
logPath = System.IO.Path.Combine(
|
||||||
|
Environment.GetFolderPath(Environment.SpecialFolder.Personal),
|
||||||
|
".config", "unity3d", "logs", "Editor.log");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(logPath) && System.IO.File.Exists(logPath))
|
||||||
|
{
|
||||||
|
// Read last few lines from the log file
|
||||||
|
var logLines = ReadLastLines(logPath, 100);
|
||||||
|
if (logLines.Count > i)
|
||||||
|
{
|
||||||
|
message = logLines[logLines.Count - 1 - i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Ignore errors in this fallback approach
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get stack trace if method available
|
||||||
|
string stackTrace = "";
|
||||||
|
if (getStackTraceMethod != null)
|
||||||
|
{
|
||||||
|
stackTrace = getStackTraceMethod.Invoke(null, new object[] { i })?.ToString() ?? "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter by type
|
||||||
|
bool typeMatch = (logType == 0 && showLogs) ||
|
||||||
|
(logType == 1 && showWarnings) ||
|
||||||
|
(logType == 2 && showErrors);
|
||||||
|
if (!typeMatch) continue;
|
||||||
|
|
||||||
|
// Filter by search term
|
||||||
|
bool searchMatch = true;
|
||||||
|
if (searchWords != null && searchWords.Length > 0)
|
||||||
|
{
|
||||||
|
string lowerMessage = message.ToLower();
|
||||||
|
string lowerStackTrace = stackTrace.ToLower();
|
||||||
|
|
||||||
|
foreach (string word in searchWords)
|
||||||
|
{
|
||||||
|
if (!lowerMessage.Contains(word) && !lowerStackTrace.Contains(word))
|
||||||
|
{
|
||||||
|
searchMatch = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!searchMatch) continue;
|
||||||
|
|
||||||
|
// Add matching entry to results
|
||||||
|
string typeStr = logType == 0 ? "Log" : logType == 1 ? "Warning" : "Error";
|
||||||
|
entries.Add(new
|
||||||
|
{
|
||||||
|
type = typeStr,
|
||||||
|
message = message,
|
||||||
|
stackTrace = stackTrace
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
// Skip entries that cause errors
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return filtered results
|
||||||
|
return new
|
||||||
|
{
|
||||||
|
message = "Console logs retrieved successfully",
|
||||||
|
entries = entries,
|
||||||
|
total_entries = count,
|
||||||
|
filtered_count = entries.Count,
|
||||||
|
show_logs = showLogs,
|
||||||
|
show_warnings = showWarnings,
|
||||||
|
show_errors = showErrors
|
||||||
|
};
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
return new
|
||||||
|
{
|
||||||
|
error = $"Failed to read console logs: {e.Message}",
|
||||||
|
entries = new List<object>()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MethodInfo FindMethod(Type type, string[] methodNames)
|
||||||
|
{
|
||||||
|
foreach (var methodName in methodNames)
|
||||||
|
{
|
||||||
|
var method = type.GetMethod(methodName,
|
||||||
|
BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
|
||||||
|
if (method != null)
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private static BuildTarget GetBuildTarget(string platform)
|
private static BuildTarget GetBuildTarget(string platform)
|
||||||
{
|
{
|
||||||
BuildTarget target;
|
BuildTarget target;
|
||||||
|
|
@ -152,4 +456,137 @@ public static class EditorControlHandler
|
||||||
}
|
}
|
||||||
return scenes.ToArray();
|
return scenes.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Helper method to get information about available properties and fields in a type
|
||||||
|
/// </summary>
|
||||||
|
private static Dictionary<string, object> GetTypeInfo(Type type)
|
||||||
|
{
|
||||||
|
var result = new Dictionary<string, object>();
|
||||||
|
|
||||||
|
// Get all public and non-public properties
|
||||||
|
var properties = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic |
|
||||||
|
BindingFlags.Static | BindingFlags.Instance);
|
||||||
|
var propList = new List<string>();
|
||||||
|
foreach (var prop in properties)
|
||||||
|
{
|
||||||
|
propList.Add($"{prop.PropertyType.Name} {prop.Name}");
|
||||||
|
}
|
||||||
|
result["Properties"] = propList;
|
||||||
|
|
||||||
|
// Get all public and non-public fields
|
||||||
|
var fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic |
|
||||||
|
BindingFlags.Static | BindingFlags.Instance);
|
||||||
|
var fieldList = new List<string>();
|
||||||
|
foreach (var field in fields)
|
||||||
|
{
|
||||||
|
fieldList.Add($"{field.FieldType.Name} {field.Name}");
|
||||||
|
}
|
||||||
|
result["Fields"] = fieldList;
|
||||||
|
|
||||||
|
// Get all public and non-public methods
|
||||||
|
var methods = type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic |
|
||||||
|
BindingFlags.Static | BindingFlags.Instance);
|
||||||
|
var methodList = new List<string>();
|
||||||
|
foreach (var method in methods)
|
||||||
|
{
|
||||||
|
if (!method.Name.StartsWith("get_") && !method.Name.StartsWith("set_"))
|
||||||
|
{
|
||||||
|
var parameters = string.Join(", ", method.GetParameters()
|
||||||
|
.Select(p => $"{p.ParameterType.Name} {p.Name}"));
|
||||||
|
methodList.Add($"{method.ReturnType.Name} {method.Name}({parameters})");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result["Methods"] = methodList;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Helper method to get all property and field values from an object
|
||||||
|
/// </summary>
|
||||||
|
private static Dictionary<string, string> GetObjectValues(object obj)
|
||||||
|
{
|
||||||
|
if (obj == null) return new Dictionary<string, string>();
|
||||||
|
|
||||||
|
var result = new Dictionary<string, string>();
|
||||||
|
var type = obj.GetType();
|
||||||
|
|
||||||
|
// Get all property values
|
||||||
|
var properties = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
|
||||||
|
foreach (var prop in properties)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var value = prop.GetValue(obj);
|
||||||
|
result[$"Property:{prop.Name}"] = value?.ToString() ?? "null";
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
result[$"Property:{prop.Name}"] = "ERROR";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all field values
|
||||||
|
var fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
|
||||||
|
foreach (var field in fields)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var value = field.GetValue(obj);
|
||||||
|
result[$"Field:{field.Name}"] = value?.ToString() ?? "null";
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
result[$"Field:{field.Name}"] = "ERROR";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads the last N lines from a file
|
||||||
|
/// </summary>
|
||||||
|
private static List<string> ReadLastLines(string filePath, int lineCount)
|
||||||
|
{
|
||||||
|
var result = new List<string>();
|
||||||
|
|
||||||
|
using (var stream = new System.IO.FileStream(filePath, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.ReadWrite))
|
||||||
|
using (var reader = new System.IO.StreamReader(stream))
|
||||||
|
{
|
||||||
|
string line;
|
||||||
|
var circularBuffer = new List<string>(lineCount);
|
||||||
|
int currentIndex = 0;
|
||||||
|
|
||||||
|
// Read all lines keeping only the last N in a circular buffer
|
||||||
|
while ((line = reader.ReadLine()) != null)
|
||||||
|
{
|
||||||
|
if (circularBuffer.Count < lineCount)
|
||||||
|
{
|
||||||
|
circularBuffer.Add(line);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
circularBuffer[currentIndex] = line;
|
||||||
|
currentIndex = (currentIndex + 1) % lineCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reorder the circular buffer so that lines are returned in order
|
||||||
|
if (circularBuffer.Count == lineCount)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < lineCount; i++)
|
||||||
|
{
|
||||||
|
result.Add(circularBuffer[(currentIndex + i) % lineCount]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result.AddRange(circularBuffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -55,6 +55,7 @@ def asset_creation_strategy() -> str:
|
||||||
"Unity MCP Server Tools and Best Practices:\n\n"
|
"Unity MCP Server Tools and Best Practices:\n\n"
|
||||||
"1. **Editor Control**\n"
|
"1. **Editor Control**\n"
|
||||||
" - `editor_action` - Performs editor-wide actions such as `PLAY`, `PAUSE`, `STOP`, `BUILD`, `SAVE`\n"
|
" - `editor_action` - Performs editor-wide actions such as `PLAY`, `PAUSE`, `STOP`, `BUILD`, `SAVE`\n"
|
||||||
|
" - `read_console(show_logs=True, show_warnings=True, show_errors=True, search_term=None)` - Read and filter Unity Console logs\n"
|
||||||
"2. **Scene Management**\n"
|
"2. **Scene Management**\n"
|
||||||
" - `get_current_scene()`, `get_scene_list()` - Get scene details\n"
|
" - `get_current_scene()`, `get_scene_list()` - Get scene details\n"
|
||||||
" - `open_scene(path)`, `save_scene(path)` - Open/save scenes\n"
|
" - `open_scene(path)`, `save_scene(path)` - Open/save scenes\n"
|
||||||
|
|
@ -97,6 +98,8 @@ def asset_creation_strategy() -> str:
|
||||||
" - Provide correct value types for properties\n"
|
" - Provide correct value types for properties\n"
|
||||||
" - Keep prefabs in dedicated folders\n"
|
" - Keep prefabs in dedicated folders\n"
|
||||||
" - Regularly apply prefab changes\n"
|
" - Regularly apply prefab changes\n"
|
||||||
|
" - Monitor console logs for errors and warnings\n"
|
||||||
|
" - Use search terms to filter console output when debugging\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Run the server
|
# Run the server
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
from mcp.server.fastmcp import FastMCP, Context
|
from mcp.server.fastmcp import FastMCP, Context
|
||||||
from typing import Optional
|
from typing import Optional, List, Dict, Any
|
||||||
from unity_connection import get_unity_connection
|
from unity_connection import get_unity_connection
|
||||||
|
|
||||||
def register_editor_tools(mcp: FastMCP):
|
def register_editor_tools(mcp: FastMCP):
|
||||||
|
|
@ -175,3 +175,95 @@ def register_editor_tools(mcp: FastMCP):
|
||||||
return response.get("message", f"Executed command: {command_name}")
|
return response.get("message", f"Executed command: {command_name}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return f"Error executing command: {str(e)}"
|
return f"Error executing command: {str(e)}"
|
||||||
|
|
||||||
|
@mcp.tool()
|
||||||
|
def read_console(
|
||||||
|
ctx: Context,
|
||||||
|
show_logs: bool = True,
|
||||||
|
show_warnings: bool = True,
|
||||||
|
show_errors: bool = True,
|
||||||
|
search_term: Optional[str] = None
|
||||||
|
) -> List[Dict[str, Any]]:
|
||||||
|
"""Read log messages from the Unity Console.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
ctx: The MCP context
|
||||||
|
show_logs: Whether to include regular log messages (default: True)
|
||||||
|
show_warnings: Whether to include warning messages (default: True)
|
||||||
|
show_errors: Whether to include error messages (default: True)
|
||||||
|
search_term: Optional text to filter logs by content. If multiple words are provided,
|
||||||
|
entries must contain all words (not necessarily in order) to be included. (default: None)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List[Dict[str, Any]]: A list of console log entries, each containing 'type', 'message', and 'stackTrace' fields
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Prepare params with only the provided values
|
||||||
|
params = {
|
||||||
|
"show_logs": show_logs,
|
||||||
|
"show_warnings": show_warnings,
|
||||||
|
"show_errors": show_errors
|
||||||
|
}
|
||||||
|
|
||||||
|
# Only add search_term if it's provided
|
||||||
|
if search_term is not None:
|
||||||
|
params["search_term"] = search_term
|
||||||
|
|
||||||
|
response = get_unity_connection().send_command("EDITOR_CONTROL", {
|
||||||
|
"command": "READ_CONSOLE",
|
||||||
|
"params": params
|
||||||
|
})
|
||||||
|
|
||||||
|
if "error" in response:
|
||||||
|
return [{
|
||||||
|
"type": "Error",
|
||||||
|
"message": f"Failed to read console: {response['error']}",
|
||||||
|
"stackTrace": response.get("stackTrace", "")
|
||||||
|
}]
|
||||||
|
|
||||||
|
entries = response.get("entries", [])
|
||||||
|
total_entries = response.get("total_entries", 0)
|
||||||
|
filtered_count = response.get("filtered_count", 0)
|
||||||
|
filter_applied = response.get("filter_applied", False)
|
||||||
|
|
||||||
|
# Add summary info
|
||||||
|
summary = []
|
||||||
|
if total_entries > 0:
|
||||||
|
summary.append(f"Total console entries: {total_entries}")
|
||||||
|
if filter_applied:
|
||||||
|
summary.append(f"Filtered entries: {filtered_count}")
|
||||||
|
if filtered_count == 0:
|
||||||
|
summary.append(f"No entries matched the search term: '{search_term}'")
|
||||||
|
else:
|
||||||
|
summary.append(f"Showing all entries")
|
||||||
|
else:
|
||||||
|
summary.append("No entries in console")
|
||||||
|
|
||||||
|
# Add filter info
|
||||||
|
filter_types = []
|
||||||
|
if show_logs: filter_types.append("logs")
|
||||||
|
if show_warnings: filter_types.append("warnings")
|
||||||
|
if show_errors: filter_types.append("errors")
|
||||||
|
if filter_types:
|
||||||
|
summary.append(f"Showing: {', '.join(filter_types)}")
|
||||||
|
|
||||||
|
# Add summary as first entry
|
||||||
|
if summary:
|
||||||
|
entries.insert(0, {
|
||||||
|
"type": "Info",
|
||||||
|
"message": " | ".join(summary),
|
||||||
|
"stackTrace": ""
|
||||||
|
})
|
||||||
|
|
||||||
|
return entries if entries else [{
|
||||||
|
"type": "Info",
|
||||||
|
"message": "No logs found in console",
|
||||||
|
"stackTrace": ""
|
||||||
|
}]
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return [{
|
||||||
|
"type": "Error",
|
||||||
|
"message": f"Error reading console: {str(e)}",
|
||||||
|
"stackTrace": ""
|
||||||
|
}]
|
||||||
Loading…
Reference in New Issue