diff --git a/MCPForUnity/Editor/Helpers/ComponentOps.cs b/MCPForUnity/Editor/Helpers/ComponentOps.cs
index 9f20330bf..33a911a1d 100644
--- a/MCPForUnity/Editor/Helpers/ComponentOps.cs
+++ b/MCPForUnity/Editor/Helpers/ComponentOps.cs
@@ -412,7 +412,7 @@ private static Type ResolveMemberType(Type componentType, string propertyName, s
private static bool SetViaSerializedProperty(Component component, string propertyName, string normalizedName, JToken value, out string error)
{
error = null;
- var so = new SerializedObject(component);
+ using var so = new SerializedObject(component);
SerializedProperty prop = so.FindProperty(propertyName)
?? so.FindProperty(normalizedName);
@@ -515,7 +515,7 @@ private static bool SetSerializedPropertyRecursive(SerializedProperty prop, JTok
return true;
case SerializedPropertyType.String:
- prop.stringValue = value == null || value.Type == JTokenType.Null ? null : value.ToString();
+ prop.stringValue = value == null || value.Type == JTokenType.Null ? string.Empty : value.ToString();
return true;
case SerializedPropertyType.Enum:
diff --git a/MCPForUnity/Editor/Helpers/GameObjectSerializer.cs b/MCPForUnity/Editor/Helpers/GameObjectSerializer.cs
index 688546b7d..da24771d6 100644
--- a/MCPForUnity/Editor/Helpers/GameObjectSerializer.cs
+++ b/MCPForUnity/Editor/Helpers/GameObjectSerializer.cs
@@ -450,11 +450,18 @@ public static object GetComponentData(Component c, bool includeNonPublicSerializ
propName == "worldToLocalMatrix" ||
propName == "localToWorldMatrix"))
{
- // McpLog.Info($"[GetComponentData] Explicitly skipping Transform property: {propName}");
skipProperty = true;
}
// --- End Skip Transform Properties ---
+ // --- Skip Collider properties that cause native crashes via PhysX ---
+ if (typeof(Collider).IsAssignableFrom(componentType) &&
+ propName == "GeometryHolder")
+ {
+ skipProperty = true;
+ }
+ // --- End Skip Collider Properties ---
+
// Skip if flagged
if (skipProperty)
{
diff --git a/MCPForUnity/Editor/Tools/ReadConsole.cs b/MCPForUnity/Editor/Tools/ReadConsole.cs
index 342f7b1fc..100938f33 100644
--- a/MCPForUnity/Editor/Tools/ReadConsole.cs
+++ b/MCPForUnity/Editor/Tools/ReadConsole.cs
@@ -30,8 +30,6 @@ public static class ReadConsole
private static FieldInfo _messageField;
private static FieldInfo _fileField;
private static FieldInfo _lineField;
- private static FieldInfo _instanceIdField;
-
// Note: Timestamp is not directly available in LogEntry; need to parse message or find alternative?
// Static constructor for reflection setup
@@ -102,10 +100,6 @@ static ReadConsole()
if (_lineField == null)
throw new Exception("Failed to reflect LogEntry.line");
- _instanceIdField = logEntryType.GetField("instanceID", instanceFlags);
- if (_instanceIdField == null)
- throw new Exception("Failed to reflect LogEntry.instanceID");
-
// (Calibration removed)
}
@@ -121,7 +115,7 @@ static ReadConsole()
_getCountMethod =
_getEntryMethod =
null;
- _modeField = _messageField = _fileField = _lineField = _instanceIdField = null;
+ _modeField = _messageField = _fileField = _lineField = null;
}
}
@@ -140,7 +134,6 @@ public static object HandleCommand(JObject @params)
|| _messageField == null
|| _fileField == null
|| _lineField == null
- || _instanceIdField == null
)
{
// Log the error here as well for easier debugging in Unity Console
@@ -290,7 +283,6 @@ bool includeStacktrace
string file = (string)_fileField.GetValue(logEntryInstance);
int line = (int)_lineField.GetValue(logEntryInstance);
- // int instanceId = (int)_instanceIdField.GetValue(logEntryInstance);
if (string.IsNullOrEmpty(message))
{
@@ -515,29 +507,6 @@ private static bool IsExplicitDebugLog(string fullMessage)
return false;
}
- ///
- /// Applies the "one level lower" remapping for filtering, like the old version.
- /// This ensures compatibility with the filtering logic that expects remapped types.
- ///
- private static LogType GetRemappedTypeForFiltering(LogType unityType)
- {
- switch (unityType)
- {
- case LogType.Error:
- return LogType.Warning; // Error becomes Warning
- case LogType.Warning:
- return LogType.Log; // Warning becomes Log
- case LogType.Assert:
- return LogType.Assert; // Assert remains Assert
- case LogType.Log:
- return LogType.Log; // Log remains Log
- case LogType.Exception:
- return LogType.Warning; // Exception becomes Warning
- default:
- return LogType.Log; // Default fallback
- }
- }
-
///
/// Attempts to extract the stack trace part from a log message.
/// Unity log messages often have the stack trace appended after the main message,
diff --git a/MCPForUnity/package.json b/MCPForUnity/package.json
index c1de0a072..fa0bfe34a 100644
--- a/MCPForUnity/package.json
+++ b/MCPForUnity/package.json
@@ -7,7 +7,8 @@
"documentationUrl": "https://github.com/CoplayDev/unity-mcp",
"licensesUrl": "https://github.com/CoplayDev/unity-mcp/blob/main/LICENSE",
"dependencies": {
- "com.unity.nuget.newtonsoft-json": "3.0.2"
+ "com.unity.nuget.newtonsoft-json": "3.0.2",
+ "com.unity.test-framework": "1.1.31"
},
"keywords": [
"unity",
diff --git a/TestProjects/UnityMCPTests/Assets/Tests/EditMode/Helpers/CodexConfigHelperTests.cs b/TestProjects/UnityMCPTests/Assets/Tests/EditMode/Helpers/CodexConfigHelperTests.cs
index 0ffda8643..32a5aef37 100644
--- a/TestProjects/UnityMCPTests/Assets/Tests/EditMode/Helpers/CodexConfigHelperTests.cs
+++ b/TestProjects/UnityMCPTests/Assets/Tests/EditMode/Helpers/CodexConfigHelperTests.cs
@@ -1,3 +1,5 @@
+using System.Collections.Generic;
+using System.Linq;
using NUnit.Framework;
using MCPForUnity.Editor.Helpers;
using MCPForUnity.External.Tommy;
@@ -10,6 +12,31 @@ namespace MCPForUnityTests.Editor.Helpers
{
public class CodexConfigHelperTests
{
+ ///
+ /// Validates that a TOML args array contains the expected uvx structure:
+ /// --from, a mcpforunityserver reference, mcp-for-unity package name,
+ /// and optionally --prerelease/explicit (only for prerelease builds).
+ ///
+ private static void AssertValidUvxArgs(TomlArray args)
+ {
+ var argValues = new List();
+ foreach (TomlNode child in args.Children)
+ argValues.Add((child as TomlString).Value);
+
+ Assert.IsTrue(argValues.Contains("--from"), "Args should contain --from");
+ Assert.IsTrue(argValues.Any(a => a.Contains("mcpforunityserver")), "Args should contain PyPI package reference");
+ Assert.IsTrue(argValues.Contains("mcp-for-unity"), "Args should contain package name");
+
+ // Prerelease builds include --prerelease explicit before --from
+ int fromIndex = argValues.IndexOf("--from");
+ int prereleaseIndex = argValues.IndexOf("--prerelease");
+ if (prereleaseIndex >= 0)
+ {
+ Assert.IsTrue(prereleaseIndex < fromIndex, "--prerelease should come before --from");
+ Assert.AreEqual("explicit", argValues[prereleaseIndex + 1], "--prerelease should be followed by explicit");
+ }
+ }
+
///
/// Mock platform service for testing
///
@@ -244,19 +271,7 @@ public void BuildCodexServerBlock_OnWindows_IncludesSystemRootEnv()
// Verify args contains the proper uvx command structure
var args = argsNode as TomlArray;
- Assert.IsTrue(args.ChildrenCount >= 5, "Args should contain --prerelease, explicit, --from, PyPI package reference, and package name");
-
- var firstArg = (args[0] as TomlString).Value;
- var secondArg = (args[1] as TomlString).Value;
- var thirdArg = (args[2] as TomlString).Value;
- var fourthArg = (args[3] as TomlString).Value;
- var fifthArg = (args[4] as TomlString).Value;
-
- Assert.AreEqual("--prerelease", firstArg, "First arg should be --prerelease");
- Assert.AreEqual("explicit", secondArg, "Second arg should be explicit");
- Assert.AreEqual("--from", thirdArg, "Third arg should be --from");
- Assert.IsTrue(fourthArg.Contains("mcpforunityserver"), "Fourth arg should be PyPI package reference");
- Assert.AreEqual("mcp-for-unity", fifthArg, "Fifth arg should be mcp-for-unity");
+ AssertValidUvxArgs(args);
// Verify env.SystemRoot is present on Windows
bool hasEnv = unityMcp.TryGetNode("env", out var envNode);
@@ -313,19 +328,7 @@ public void BuildCodexServerBlock_OnNonWindows_ExcludesEnv()
// Verify args contains the proper uvx command structure
var args = argsNode as TomlArray;
- Assert.IsTrue(args.ChildrenCount >= 5, "Args should contain --prerelease, explicit, --from, PyPI package reference, and package name");
-
- var firstArg = (args[0] as TomlString).Value;
- var secondArg = (args[1] as TomlString).Value;
- var thirdArg = (args[2] as TomlString).Value;
- var fourthArg = (args[3] as TomlString).Value;
- var fifthArg = (args[4] as TomlString).Value;
-
- Assert.AreEqual("--prerelease", firstArg, "First arg should be --prerelease");
- Assert.AreEqual("explicit", secondArg, "Second arg should be explicit");
- Assert.AreEqual("--from", thirdArg, "Third arg should be --from");
- Assert.IsTrue(fourthArg.Contains("mcpforunityserver"), "Fourth arg should be PyPI package reference");
- Assert.AreEqual("mcp-for-unity", fifthArg, "Fifth arg should be mcp-for-unity");
+ AssertValidUvxArgs(args);
// Verify env is NOT present on non-Windows platforms
bool hasEnv = unityMcp.TryGetNode("env", out _);
@@ -384,19 +387,7 @@ public void UpsertCodexServerBlock_OnWindows_IncludesSystemRootEnv()
// Verify args contains the proper uvx command structure
var args = argsNode as TomlArray;
- Assert.IsTrue(args.ChildrenCount >= 5, "Args should contain --prerelease, explicit, --from, PyPI package reference, and package name");
-
- var firstArg = (args[0] as TomlString).Value;
- var secondArg = (args[1] as TomlString).Value;
- var thirdArg = (args[2] as TomlString).Value;
- var fourthArg = (args[3] as TomlString).Value;
- var fifthArg = (args[4] as TomlString).Value;
-
- Assert.AreEqual("--prerelease", firstArg, "First arg should be --prerelease");
- Assert.AreEqual("explicit", secondArg, "Second arg should be explicit");
- Assert.AreEqual("--from", thirdArg, "Third arg should be --from");
- Assert.IsTrue(fourthArg.Contains("mcpforunityserver"), "Fourth arg should be PyPI package reference");
- Assert.AreEqual("mcp-for-unity", fifthArg, "Fifth arg should be mcp-for-unity");
+ AssertValidUvxArgs(args);
// Verify env.SystemRoot is present on Windows
bool hasEnv = unityMcp.TryGetNode("env", out var envNode);
@@ -462,19 +453,7 @@ public void UpsertCodexServerBlock_OnNonWindows_ExcludesEnv()
// Verify args contains the proper uvx command structure
var args = argsNode as TomlArray;
- Assert.IsTrue(args.ChildrenCount >= 5, "Args should contain --prerelease, explicit, --from, PyPI package reference, and package name");
-
- var firstArg = (args[0] as TomlString).Value;
- var secondArg = (args[1] as TomlString).Value;
- var thirdArg = (args[2] as TomlString).Value;
- var fourthArg = (args[3] as TomlString).Value;
- var fifthArg = (args[4] as TomlString).Value;
-
- Assert.AreEqual("--prerelease", firstArg, "First arg should be --prerelease");
- Assert.AreEqual("explicit", secondArg, "Second arg should be explicit");
- Assert.AreEqual("--from", thirdArg, "Third arg should be --from");
- Assert.IsTrue(fourthArg.Contains("mcpforunityserver"), "Fourth arg should be PyPI package reference");
- Assert.AreEqual("mcp-for-unity", fifthArg, "Fifth arg should be mcp-for-unity");
+ AssertValidUvxArgs(args);
// Verify env is NOT present on non-Windows platforms
bool hasEnv = unityMcp.TryGetNode("env", out _);