From 075d68dba331533c87ee62bd7ade8d34db6ac44e Mon Sep 17 00:00:00 2001 From: dsarno Date: Thu, 23 Oct 2025 18:25:29 -0700 Subject: [PATCH] Material tools: support direct shader property keys + add EditMode coverage (#344) * Add TDD tests for MCP material management issues - MCPMaterialTests.cs: Tests for material creation, assignment, and data reading - MCPParameterHandlingTests.cs: Tests for JSON parameter parsing issues - SphereMaterialWorkflowTests.cs: Tests for complete sphere material workflow These tests document the current issues with: - JSON parameter parsing in manage_asset and manage_gameobject tools - Material creation with properties - Material assignment to GameObjects - Material component data reading All tests currently fail (Red phase of TDD) and serve as specifications for what needs to be fixed in the MCP system. * Refine TDD tests to focus on actual MCP tool parameter parsing issues - Removed redundant tests that verify working functionality (GameObjectSerializer, Unity APIs) - Kept focused tests that document the real issue: MCP tool parameter validation - Tests now clearly identify the root cause: JSON string parsing in MCP tools - Tests specify exactly what needs to be fixed: parameter type flexibility The issue is NOT in Unity APIs or serialization (which work fine), but in MCP tool parameter validation being too strict. * Fix port discovery protocol mismatch - Update _try_probe_unity_mcp to recognize Unity bridge welcome message - Unity bridge sends 'WELCOME UNITY-MCP' instead of JSON pong response - Maintains backward compatibility with JSON pong format - Fixes MCP server connection to Unity Editor * Resolve merge: unify manage_gameobject param coercion and schema widening * Tests: add MaterialParameterToolTests; merge port probe fix; widen tool schemas for JSON-string params * feat(material): support direct shader property keys and texture paths; add EditMode tests * chore(tests): track required .meta files; remove ephemeral Assets/Editor test helper * fix(manage_gameobject): validate parsed component_properties is a dict; return clear error --- MCPForUnity/Editor/Tools/ManageAsset.cs | 117 ++++++++++++- MCPForUnity/Editor/Tools/ManageGameObject.cs | 14 +- .../UnityMcpServer~/src/tools/manage_asset.py | 2 +- .../src/tools/manage_gameobject.py | 4 +- .../UnityMCPTests/Assets/Temp/LiveTests.meta | 8 + .../Assets/Temp/MCPToolParameterTests.meta | 8 + .../Temp/MaterialDirectPropertiesTests.meta | 8 + .../Temp/MaterialParameterToolTests.meta | 8 + .../Tests/EditMode/MCPToolParameterTests.cs | 27 +-- .../Tools/MaterialDirectPropertiesTests.cs | 158 ++++++++++++++++++ .../MaterialDirectPropertiesTests.cs.meta | 11 ++ .../Tools/MaterialParameterToolTests.cs | 49 +----- .../UnityMCPTests/Packages/manifest.json | 2 +- .../UnityMCPTests/ProjectSettings/boot.config | 0 14 files changed, 357 insertions(+), 59 deletions(-) create mode 100644 TestProjects/UnityMCPTests/Assets/Temp/LiveTests.meta create mode 100644 TestProjects/UnityMCPTests/Assets/Temp/MCPToolParameterTests.meta create mode 100644 TestProjects/UnityMCPTests/Assets/Temp/MaterialDirectPropertiesTests.meta create mode 100644 TestProjects/UnityMCPTests/Assets/Temp/MaterialParameterToolTests.meta create mode 100644 TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/MaterialDirectPropertiesTests.cs create mode 100644 TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/MaterialDirectPropertiesTests.cs.meta delete mode 100644 TestProjects/UnityMCPTests/ProjectSettings/boot.config diff --git a/MCPForUnity/Editor/Tools/ManageAsset.cs b/MCPForUnity/Editor/Tools/ManageAsset.cs index dbf49f2..770fd24 100644 --- a/MCPForUnity/Editor/Tools/ManageAsset.cs +++ b/MCPForUnity/Editor/Tools/ManageAsset.cs @@ -64,7 +64,19 @@ namespace MCPForUnity.Editor.Tools string path = @params["path"]?.ToString(); // Coerce string JSON to JObject for 'properties' if provided as a JSON string - JsonUtil.CoerceJsonStringParameter(@params, "properties"); + var propertiesToken = @params["properties"]; + if (propertiesToken != null && propertiesToken.Type == JTokenType.String) + { + try + { + var parsed = JObject.Parse(propertiesToken.ToString()); + @params["properties"] = parsed; + } + catch (Exception e) + { + Debug.LogWarning($"[ManageAsset] Could not parse 'properties' JSON string: {e.Message}"); + } + } try { @@ -1002,7 +1014,108 @@ namespace MCPForUnity.Editor.Tools } } - // TODO: Add handlers for other property types (Vectors, Ints, Keywords, RenderQueue, etc.) + // --- Flexible direct property assignment --- + // Allow payloads like: { "_Color": [r,g,b,a] }, { "_Glossiness": 0.5 }, { "_MainTex": "Assets/.." } + // while retaining backward compatibility with the structured keys above. + // This iterates all top-level keys except the reserved structured ones and applies them + // if they match known shader properties. + var reservedKeys = new HashSet(StringComparer.OrdinalIgnoreCase) { "shader", "color", "float", "texture" }; + + // Helper resolves common URP/Standard aliasing (e.g., _Color <-> _BaseColor, _MainTex <-> _BaseMap, _Glossiness <-> _Smoothness) + string ResolvePropertyName(string name) + { + if (string.IsNullOrEmpty(name)) return name; + string[] candidates; + switch (name) + { + case "_Color": candidates = new[] { "_Color", "_BaseColor" }; break; + case "_BaseColor": candidates = new[] { "_BaseColor", "_Color" }; break; + case "_MainTex": candidates = new[] { "_MainTex", "_BaseMap" }; break; + case "_BaseMap": candidates = new[] { "_BaseMap", "_MainTex" }; break; + case "_Glossiness": candidates = new[] { "_Glossiness", "_Smoothness" }; break; + case "_Smoothness": candidates = new[] { "_Smoothness", "_Glossiness" }; break; + default: candidates = new[] { name }; break; + } + foreach (var candidate in candidates) + { + if (mat.HasProperty(candidate)) return candidate; + } + return name; // fall back to original + } + + foreach (var prop in properties.Properties()) + { + if (reservedKeys.Contains(prop.Name)) continue; + string shaderProp = ResolvePropertyName(prop.Name); + JToken v = prop.Value; + + // Color: numeric array [r,g,b,(a)] + if (v is JArray arr && arr.Count >= 3 && arr.All(t => t.Type == JTokenType.Float || t.Type == JTokenType.Integer)) + { + if (mat.HasProperty(shaderProp)) + { + try + { + var c = new Color( + arr[0].ToObject(), + arr[1].ToObject(), + arr[2].ToObject(), + arr.Count > 3 ? arr[3].ToObject() : 1f + ); + if (mat.GetColor(shaderProp) != c) + { + mat.SetColor(shaderProp, c); + modified = true; + } + } + catch (Exception ex) + { + Debug.LogWarning($"Error setting color '{shaderProp}': {ex.Message}"); + } + } + continue; + } + + // Float: single number + if (v.Type == JTokenType.Float || v.Type == JTokenType.Integer) + { + if (mat.HasProperty(shaderProp)) + { + try + { + float f = v.ToObject(); + if (!Mathf.Approximately(mat.GetFloat(shaderProp), f)) + { + mat.SetFloat(shaderProp, f); + modified = true; + } + } + catch (Exception ex) + { + Debug.LogWarning($"Error setting float '{shaderProp}': {ex.Message}"); + } + } + continue; + } + + // Texture: string path + if (v.Type == JTokenType.String) + { + string texPath = v.ToString(); + if (!string.IsNullOrEmpty(texPath) && mat.HasProperty(shaderProp)) + { + var tex = AssetDatabase.LoadAssetAtPath(AssetPathUtility.SanitizeAssetPath(texPath)); + if (tex != null && mat.GetTexture(shaderProp) != tex) + { + mat.SetTexture(shaderProp, tex); + modified = true; + } + } + continue; + } + } + + // TODO: Add handlers for other property types (Vectors, Ints, Keywords, RenderQueue, etc.) return modified; } diff --git a/MCPForUnity/Editor/Tools/ManageGameObject.cs b/MCPForUnity/Editor/Tools/ManageGameObject.cs index b6219ec..1ad4107 100644 --- a/MCPForUnity/Editor/Tools/ManageGameObject.cs +++ b/MCPForUnity/Editor/Tools/ManageGameObject.cs @@ -67,7 +67,19 @@ namespace MCPForUnity.Editor.Tools // --- End add parameter --- // Coerce string JSON to JObject for 'componentProperties' if provided as a JSON string - JsonUtil.CoerceJsonStringParameter(@params, "componentProperties"); + var componentPropsToken = @params["componentProperties"]; + if (componentPropsToken != null && componentPropsToken.Type == JTokenType.String) + { + try + { + var parsed = JObject.Parse(componentPropsToken.ToString()); + @params["componentProperties"] = parsed; + } + catch (Exception e) + { + Debug.LogWarning($"[ManageGameObject] Could not parse 'componentProperties' JSON string: {e.Message}"); + } + } // --- Prefab Redirection Check --- string targetPath = diff --git a/MCPForUnity/UnityMcpServer~/src/tools/manage_asset.py b/MCPForUnity/UnityMcpServer~/src/tools/manage_asset.py index 4810dab..a577e94 100644 --- a/MCPForUnity/UnityMcpServer~/src/tools/manage_asset.py +++ b/MCPForUnity/UnityMcpServer~/src/tools/manage_asset.py @@ -39,7 +39,7 @@ async def manage_asset( try: properties = json.loads(properties) ctx.info("manage_asset: coerced properties from JSON string to dict") - except json.JSONDecodeError as e: + except Exception as e: ctx.warn(f"manage_asset: failed to parse properties JSON string: {e}") # Leave properties as-is; Unity side may handle defaults # Ensure properties is a dict if None diff --git a/MCPForUnity/UnityMcpServer~/src/tools/manage_gameobject.py b/MCPForUnity/UnityMcpServer~/src/tools/manage_gameobject.py index f74d142..794013b 100644 --- a/MCPForUnity/UnityMcpServer~/src/tools/manage_gameobject.py +++ b/MCPForUnity/UnityMcpServer~/src/tools/manage_gameobject.py @@ -45,7 +45,6 @@ def manage_gameobject( "List of component names to remove"] | None = None, component_properties: Annotated[dict[str, dict[str, Any]] | str, """Dictionary of component names to their properties to set. For example: - Can also be provided as a JSON string representation of the dict. `{"MyScript": {"otherObject": {"find": "Player", "method": "by_name"}}}` assigns GameObject `{"MyScript": {"playerHealth": {"find": "Player", "component": "HealthComponent"}}}` assigns Component Example set nested property: @@ -122,6 +121,9 @@ def manage_gameobject( ctx.info("manage_gameobject: coerced component_properties from JSON string to dict") except json.JSONDecodeError as e: return {"success": False, "message": f"Invalid JSON in component_properties: {e}"} + # Ensure final type is a dict (object) if provided + if component_properties is not None and not isinstance(component_properties, dict): + return {"success": False, "message": "component_properties must be a JSON object (dict)."} try: # Map tag to search_term when search_method is by_tag for backward compatibility if action == "find" and search_method == "by_tag" and tag is not None and search_term is None: diff --git a/TestProjects/UnityMCPTests/Assets/Temp/LiveTests.meta b/TestProjects/UnityMCPTests/Assets/Temp/LiveTests.meta new file mode 100644 index 0000000..16c8bb6 --- /dev/null +++ b/TestProjects/UnityMCPTests/Assets/Temp/LiveTests.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0c392d9059b864f608a4d32e4347c3d6 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/TestProjects/UnityMCPTests/Assets/Temp/MCPToolParameterTests.meta b/TestProjects/UnityMCPTests/Assets/Temp/MCPToolParameterTests.meta new file mode 100644 index 0000000..fd2be78 --- /dev/null +++ b/TestProjects/UnityMCPTests/Assets/Temp/MCPToolParameterTests.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d5876265244e44b0dbea3a1351bf24be +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/TestProjects/UnityMCPTests/Assets/Temp/MaterialDirectPropertiesTests.meta b/TestProjects/UnityMCPTests/Assets/Temp/MaterialDirectPropertiesTests.meta new file mode 100644 index 0000000..baddc2b --- /dev/null +++ b/TestProjects/UnityMCPTests/Assets/Temp/MaterialDirectPropertiesTests.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e6bcc993a43284ece98cc3f36f2dc8ab +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/TestProjects/UnityMCPTests/Assets/Temp/MaterialParameterToolTests.meta b/TestProjects/UnityMCPTests/Assets/Temp/MaterialParameterToolTests.meta new file mode 100644 index 0000000..f0a87d3 --- /dev/null +++ b/TestProjects/UnityMCPTests/Assets/Temp/MaterialParameterToolTests.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 91e200a37b70f45b28121bae6a351cf6 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/TestProjects/UnityMCPTests/Assets/Tests/EditMode/MCPToolParameterTests.cs b/TestProjects/UnityMCPTests/Assets/Tests/EditMode/MCPToolParameterTests.cs index a5d3c20..325e200 100644 --- a/TestProjects/UnityMCPTests/Assets/Tests/EditMode/MCPToolParameterTests.cs +++ b/TestProjects/UnityMCPTests/Assets/Tests/EditMode/MCPToolParameterTests.cs @@ -18,24 +18,21 @@ namespace Tests.EditMode /// public class MCPToolParameterTests { - private const string TempDir = "Assets/Temp/MCPToolParameterTests"; - - [SetUp] - public void SetUp() + [Test] + public void Test_ManageAsset_ShouldAcceptJSONProperties() { + // Arrange: create temp folder + const string tempDir = "Assets/Temp/MCPToolParameterTests"; if (!AssetDatabase.IsValidFolder("Assets/Temp")) { AssetDatabase.CreateFolder("Assets", "Temp"); } - if (!AssetDatabase.IsValidFolder(TempDir)) + if (!AssetDatabase.IsValidFolder(tempDir)) { AssetDatabase.CreateFolder("Assets/Temp", "MCPToolParameterTests"); } - } - [Test] - public void Test_ManageAsset_ShouldAcceptJSONProperties() - { - var matPath = $"{TempDir}/JsonMat_{Guid.NewGuid().ToString("N")}.mat"; + + var matPath = $"{tempDir}/JsonMat_{Guid.NewGuid().ToString("N")}.mat"; // Build params with properties as a JSON string (to be coerced) var p = new JObject @@ -73,7 +70,10 @@ namespace Tests.EditMode [Test] public void Test_ManageGameObject_ShouldAcceptJSONComponentProperties() { - var matPath = $"{TempDir}/JsonMat_{Guid.NewGuid().ToString("N")}.mat"; + const string tempDir = "Assets/Temp/MCPToolParameterTests"; + if (!AssetDatabase.IsValidFolder("Assets/Temp")) AssetDatabase.CreateFolder("Assets", "Temp"); + if (!AssetDatabase.IsValidFolder(tempDir)) AssetDatabase.CreateFolder("Assets/Temp", "MCPToolParameterTests"); + var matPath = $"{tempDir}/JsonMat_{Guid.NewGuid().ToString("N")}.mat"; // Create a material first (object-typed properties) var createMat = new JObject @@ -121,7 +121,10 @@ namespace Tests.EditMode [Test] public void Test_JSONParsing_ShouldWorkInMCPTools() { - var matPath = $"{TempDir}/JsonMat_{Guid.NewGuid().ToString("N")}.mat"; + const string tempDir = "Assets/Temp/MCPToolParameterTests"; + if (!AssetDatabase.IsValidFolder("Assets/Temp")) AssetDatabase.CreateFolder("Assets", "Temp"); + if (!AssetDatabase.IsValidFolder(tempDir)) AssetDatabase.CreateFolder("Assets/Temp", "MCPToolParameterTests"); + var matPath = $"{tempDir}/JsonMat_{Guid.NewGuid().ToString("N")}.mat"; // manage_asset with JSON string properties (coercion path) var createMat = new JObject diff --git a/TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/MaterialDirectPropertiesTests.cs b/TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/MaterialDirectPropertiesTests.cs new file mode 100644 index 0000000..d92f0f8 --- /dev/null +++ b/TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/MaterialDirectPropertiesTests.cs @@ -0,0 +1,158 @@ +using System; +using System.IO; +using Newtonsoft.Json.Linq; +using NUnit.Framework; +using UnityEditor; +using UnityEngine; +using MCPForUnity.Editor.Tools; + +namespace MCPForUnityTests.Editor.Tools +{ + public class MaterialDirectPropertiesTests + { + private const string TempRoot = "Assets/Temp/MaterialDirectPropertiesTests"; + private string _matPath; + private string _baseMapPath; + private string _normalMapPath; + private string _occlusionMapPath; + + [SetUp] + public void SetUp() + { + if (!AssetDatabase.IsValidFolder("Assets/Temp")) + { + AssetDatabase.CreateFolder("Assets", "Temp"); + } + if (!AssetDatabase.IsValidFolder(TempRoot)) + { + AssetDatabase.CreateFolder("Assets/Temp", "MaterialDirectPropertiesTests"); + } + + string guid = Guid.NewGuid().ToString("N"); + _matPath = $"{TempRoot}/DirectProps_{guid}.mat"; + _baseMapPath = $"{TempRoot}/TexBase_{guid}.asset"; + _normalMapPath = $"{TempRoot}/TexNormal_{guid}.asset"; + _occlusionMapPath = $"{TempRoot}/TexOcc_{guid}.asset"; + + // Clean any leftovers just in case + TryDeleteAsset(_matPath); + TryDeleteAsset(_baseMapPath); + TryDeleteAsset(_normalMapPath); + TryDeleteAsset(_occlusionMapPath); + + AssetDatabase.Refresh(); + } + + [TearDown] + public void TearDown() + { + TryDeleteAsset(_matPath); + TryDeleteAsset(_baseMapPath); + TryDeleteAsset(_normalMapPath); + TryDeleteAsset(_occlusionMapPath); + AssetDatabase.Refresh(); + } + + private static void TryDeleteAsset(string path) + { + if (string.IsNullOrEmpty(path)) return; + if (AssetDatabase.LoadAssetAtPath(path) != null) + { + AssetDatabase.DeleteAsset(path); + } + var abs = Path.Combine(Directory.GetCurrentDirectory(), path); + try + { + if (File.Exists(abs)) File.Delete(abs); + if (File.Exists(abs + ".meta")) File.Delete(abs + ".meta"); + } + catch { } + } + + private static Texture2D CreateSolidTextureAsset(string path, Color color) + { + var tex = new Texture2D(4, 4, TextureFormat.RGBA32, false); + var pixels = new Color[16]; + for (int i = 0; i < pixels.Length; i++) pixels[i] = color; + tex.SetPixels(pixels); + tex.Apply(); + AssetDatabase.CreateAsset(tex, path); + AssetDatabase.SaveAssets(); + return tex; + } + + private static JObject ToJObject(object result) + { + return result as JObject ?? JObject.FromObject(result); + } + + [Test] + public void CreateAndModifyMaterial_WithDirectPropertyKeys_Works() + { + // Arrange: create textures as assets + CreateSolidTextureAsset(_baseMapPath, Color.white); + CreateSolidTextureAsset(_normalMapPath, new Color(0.5f, 0.5f, 1f)); + CreateSolidTextureAsset(_occlusionMapPath, Color.gray); + + // Create material using direct keys via JSON string + var createParams = new JObject + { + ["action"] = "create", + ["path"] = _matPath, + ["assetType"] = "Material", + ["properties"] = new JObject + { + ["shader"] = "Universal Render Pipeline/Lit", + ["_Color"] = new JArray(0f, 1f, 0f, 1f), + ["_Glossiness"] = 0.25f + } + }; + var createRes = ToJObject(ManageAsset.HandleCommand(createParams)); + Assert.IsTrue(createRes.Value("success"), createRes.ToString()); + + // Modify with aliases and textures + var modifyParams = new JObject + { + ["action"] = "modify", + ["path"] = _matPath, + ["properties"] = new JObject + { + ["_BaseColor"] = new JArray(0f, 0f, 1f, 1f), + ["_Smoothness"] = 0.5f, + ["_BaseMap"] = _baseMapPath, + ["_BumpMap"] = _normalMapPath, + ["_OcclusionMap"] = _occlusionMapPath + } + }; + var modifyRes = ToJObject(ManageAsset.HandleCommand(modifyParams)); + Assert.IsTrue(modifyRes.Value("success"), modifyRes.ToString()); + + var mat = AssetDatabase.LoadAssetAtPath(_matPath); + Assert.IsNotNull(mat, "Material should exist at path."); + + // Verify color alias applied + if (mat.HasProperty("_BaseColor")) + { + Assert.AreEqual(Color.blue, mat.GetColor("_BaseColor")); + } + else if (mat.HasProperty("_Color")) + { + Assert.AreEqual(Color.blue, mat.GetColor("_Color")); + } + + // Verify float + string smoothProp = mat.HasProperty("_Smoothness") ? "_Smoothness" : (mat.HasProperty("_Glossiness") ? "_Glossiness" : null); + Assert.IsNotNull(smoothProp, "Material should expose Smoothness/Glossiness."); + Assert.That(Mathf.Abs(mat.GetFloat(smoothProp) - 0.5f) < 1e-4f); + + // Verify textures + string baseMapProp = mat.HasProperty("_BaseMap") ? "_BaseMap" : (mat.HasProperty("_MainTex") ? "_MainTex" : null); + Assert.IsNotNull(baseMapProp, "Material should expose BaseMap/MainTex."); + Assert.IsNotNull(mat.GetTexture(baseMapProp), "BaseMap/MainTex should be assigned."); + if (mat.HasProperty("_BumpMap")) Assert.IsNotNull(mat.GetTexture("_BumpMap")); + if (mat.HasProperty("_OcclusionMap")) Assert.IsNotNull(mat.GetTexture("_OcclusionMap")); + } + } +} + + diff --git a/TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/MaterialDirectPropertiesTests.cs.meta b/TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/MaterialDirectPropertiesTests.cs.meta new file mode 100644 index 0000000..9c806e8 --- /dev/null +++ b/TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/MaterialDirectPropertiesTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3623619548eef40568ac5e3cef4c22a5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/MaterialParameterToolTests.cs b/TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/MaterialParameterToolTests.cs index f37cbf9..1ae73e0 100644 --- a/TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/MaterialParameterToolTests.cs +++ b/TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/MaterialParameterToolTests.cs @@ -96,25 +96,13 @@ namespace MCPForUnityTests.Editor.Tools } } - private void CreateTestMaterial() + [Test] + public void AssignMaterial_ToSphere_UsingComponentPropertiesObject_Succeeds() { - var createParams = new JObject - { - ["action"] = "create", - ["path"] = _matPath, - ["assetType"] = "Material", - ["properties"] = new JObject - { - ["shader"] = "Universal Render Pipeline/Lit", - ["color"] = new JArray(0f, 0f, 1f, 1f) - } - }; - var result = ToJObject(ManageAsset.HandleCommand(createParams)); - Assert.IsTrue(result.Value("success"), result.Value("error")); - } + // Ensure material exists first + CreateMaterial_WithObjectProperties_SucceedsAndSetsColor(); - private void CreateSphere() - { + // Create a sphere via handler var createGo = new JObject { ["action"] = "create", @@ -123,18 +111,9 @@ namespace MCPForUnityTests.Editor.Tools }; var createGoResult = ToJObject(ManageGameObject.HandleCommand(createGo)); Assert.IsTrue(createGoResult.Value("success"), createGoResult.Value("error")); + _sphere = GameObject.Find("ToolTestSphere"); Assert.IsNotNull(_sphere, "Sphere should be created."); - } - - [Test] - public void AssignMaterial_ToSphere_UsingComponentPropertiesObject_Succeeds() - { - CreateTestMaterial(); - CreateSphere(); - - // Create a sphere via handler - // Assign material via object-typed componentProperties var modifyParams = new JObject @@ -163,20 +142,8 @@ namespace MCPForUnityTests.Editor.Tools [Test] public void ReadRendererData_DoesNotInstantiateMaterial_AndIncludesSharedMaterial() { - CreateTestMaterial(); - CreateSphere(); - var modifyParams = new JObject - { - ["action"] = "modify", - ["target"] = "ToolTestSphere", - ["searchMethod"] = "by_name", - ["componentProperties"] = new JObject - { - ["MeshRenderer"] = new JObject { ["sharedMaterial"] = _matPath } - } - }; - var modifyResult = ToJObject(ManageGameObject.HandleCommand(modifyParams)); - Assert.IsTrue(modifyResult.Value("success"), modifyResult.Value("error")); + // Prepare object and assignment + AssignMaterial_ToSphere_UsingComponentPropertiesObject_Succeeds(); var renderer = _sphere.GetComponent(); int beforeId = renderer.sharedMaterial != null ? renderer.sharedMaterial.GetInstanceID() : 0; diff --git a/TestProjects/UnityMCPTests/Packages/manifest.json b/TestProjects/UnityMCPTests/Packages/manifest.json index 0bd78a6..f736165 100644 --- a/TestProjects/UnityMCPTests/Packages/manifest.json +++ b/TestProjects/UnityMCPTests/Packages/manifest.json @@ -1,6 +1,6 @@ { "dependencies": { - "com.coplaydev.unity-mcp": "file:../../../MCPForUnity", + "com.coplaydev.unity-mcp": "file:/Users/davidsarno/unity-mcp/MCPForUnity", "com.unity.ai.navigation": "1.1.4", "com.unity.collab-proxy": "2.5.2", "com.unity.feature.development": "1.0.1", diff --git a/TestProjects/UnityMCPTests/ProjectSettings/boot.config b/TestProjects/UnityMCPTests/ProjectSettings/boot.config deleted file mode 100644 index e69de29..0000000