Skip to content

Conversation

@dsarno
Copy link
Collaborator

@dsarno dsarno commented Jan 7, 2026

Summary

This PR significantly hardens the ManageScriptableObject tool with better path handling, auto-resize arrays, extended type support (AnimationCurve, Quaternion), and dry-run validation. Also consolidates test utility code to eliminate duplication across 11 test files.

Key Changes

🔧 ManageScriptableObject Tool Hardening

Path Normalization

  • Friendly array syntax: myList[5]myList.Array.data[5] (auto-normalized)
  • Robust path sanitization and validation

Auto-Resize Arrays

  • Automatically resize arrays when setting elements beyond current bounds
  • No more "property not found" errors for out-of-bounds indices

Extended Type Support

  • AnimationCurve support via JSON keyframe arrays:
    {"keys": [{"time": 0, "value": 0}, {"time": 1, "value": 1}]}
  • Quaternion support via Euler angles [x, y, z] or raw components [x, y, z, w]

Dry-Run Validation

  • dryRun: true parameter validates patches without applying changes
  • Returns per-patch validation results for debugging

🧪 Test Infrastructure

New TestUtilities.cs - Consolidates duplicate helpers from 11 test files:

  • ToJObject() - safe JSON conversion
  • EnsureFolder() - recursive folder creation
  • WaitForUnityReady() - compilation wait helper
  • FindFallbackShader() - cross-pipeline shader fallback
  • CleanupEmptyParentFolders() - TearDown cleanup

New Stress Test Suite (ManageScriptableObjectStressTests.cs - 1,100 lines):

  • Big bang test with 50+ nested array elements
  • Auto-resize boundary tests
  • Friendly path syntax tests
  • Deep nesting (3+ levels)
  • Mixed type operations
  • Rapid fire sequential modifications
  • Type mismatch error handling
  • AnimationCurve keyframe tests
  • Quaternion format tests (Euler, raw, object)
  • Dry-run validation tests

Files Changed

Category Files Lines
Core Tool ManageScriptableObject.cs +903
Python Wrapper manage_scriptable_object.py +3
Test Utilities TestUtilities.cs +140
Stress Tests ManageScriptableObjectStressTests.cs +1,100
Test Fixtures ComplexStressSO.cs, ArrayStressSO.cs, DeepStressSO.cs +89
Test Cleanup 11 existing test files -350 (deduplication)

Total: 25 files, +2,250 / -350 lines

Testing

  • ✅ 260 Unity EditMode tests pass
  • ✅ 119 Python integration tests pass
  • ✅ New stress tests cover all hardening features

Breaking Changes

None - all changes are backward compatible.


Summary by Sourcery

Harden the ManageScriptableObject tool with safer path handling, auto-growing arrays, extended type support, and dry-run validation, while consolidating shared Unity test utilities and adding comprehensive stress tests.

New Features:

  • Add dry-run validation mode for ManageScriptableObject modify operations, returning per-patch validation results without applying changes.
  • Support friendly array property paths that are normalized to Unity's internal SerializedProperty format.
  • Enable automatic array resizing when setting elements beyond current bounds in ScriptableObject patches.
  • Add AnimationCurve and Quaternion value parsing and setting support in ScriptableObject patches.
  • Allow shorthand object reference resolution from plain GUID or asset path strings in patches.

Enhancements:

  • Refine ScriptableObject patch application to use shared ParamCoercion and VectorParsing helpers and provide more descriptive errors for unsupported property types.
  • Expand VectorParsing helpers with Vector4 support used by the ScriptableObject tool.

Tests:

  • Introduce a large ManageScriptableObjectStressTests suite covering bulk operations, auto-resize behavior, nested paths, mixed types, GUID shorthand, AnimationCurve, Quaternion, and dry-run scenarios.
  • Add new ScriptableObject fixtures (ComplexStressSO, ArrayStressSO, DeepStressSO) to support stress and feature tests.
  • Extract common EditMode test utilities (JSON conversion, folder management, Unity compilation waits, shader selection, cleanup helpers) into a shared TestUtilities class and update existing tests to use it.
  • Extend Python integration tests for manage_scriptable_object to cover forwarding and coercion of the dry_run parameter.

Summary by CodeRabbit

Release Notes

  • New Features

    • Added support for Vector4, Quaternion, and AnimationCurve property modifications with flexible input formats
    • Introduced dry-run mode to validate property patches without applying changes
    • Enabled friendly path syntax (e.g., array[5]) for array element access
    • Added automatic array capacity expansion for out-of-bounds modifications
  • Improvements

    • Enhanced error messaging for object reference resolution with explicit method disclosure

✏️ Tip: You can customize this high-level summary in your review settings.

dsarno added 4 commits January 6, 2026 19:13
…auto-resize, bulk mapping

Phase 1: Path Syntax & Auto-Resizing
- Add NormalizePropertyPath() to convert field[index] to Array.data format
- Add EnsureArrayCapacity() to auto-grow arrays when targeting out-of-bounds indices

Phase 2: Consolidation
- Replace duplicate TryGet* helpers with ParamCoercion/VectorParsing shared utilities
- Add Vector4 parsing support to VectorParsing.cs

Phase 3: Bulk Data Mapping
- Handle JArray values for list/array properties (recursive element setting)
- Handle JObject values for nested struct/class properties

Phase 4: Enhanced Reference Resolution
- Support plain 32-char GUID strings for ObjectReference fields

Phase 5: Validation & Dry-Run
- Add ValidatePatches() for pre-validation of all patches
- Add dry_run parameter to validate without mutating

Includes comprehensive stress test suite covering:
- Big Bang (large nested arrays), Out of Bounds, Friendly Path Syntax
- Deep Nesting, Mixed References, Rapid Fire, Type Mismatch
- Bulk Array Mapping, GUID Shorthand, Dry Run validation
…object tool

- Implement TrySetAnimationCurve() supporting both {'keys': [...]} and direct [...] formats
  * Support keyframe properties: time, value, inSlope, outSlope, weightedMode, inWeight, outWeight
  * Gracefully default missing optional fields to 0
  * Clear error messages for malformed structures

- Implement TrySetQuaternion() with 4 input formats:
  * Euler array [x, y, z] - 3 elements interpreted as degrees
  * Raw array [x, y, z, w] - 4 components
  * Object format {x, y, z, w} - explicit components
  * Explicit euler {euler: [x, y, z]} - labeled format

- Improve error handling:
  * Null values: AnimationCurve→empty, Quaternion→identity
  * Invalid inputs rejected with specific, actionable error messages
  * Validate keyframe objects and array sizes

- Add comprehensive test coverage in ManageScriptableObjectStressTests.cs:
  * AnimationCurve with keyframe array format
  * AnimationCurve with direct array (no wrapper)
  * Quaternion via Euler angles
  * Quaternion via raw components
  * Quaternion via object format
  * Quaternion via explicit euler property

- Fix test file compilation issues:
  * Replace undefined TestFolder with _runRoot
  * Add System.IO using statement
- Add TestUtilities.cs with shared helpers:
  - ToJObject() - consolidates 11 duplicates across test files
  - EnsureFolder() - consolidates 2 duplicates
  - WaitForUnityReady() - consolidates 2 duplicates
  - FindFallbackShader() - consolidates shader chain duplicates
  - SafeDeleteAsset() - helper for asset cleanup
  - CleanupEmptyParentFolders() - standardizes TearDown cleanup

- Update 11 test files to use shared TestUtilities via 'using static'
- Standardize TearDown cleanup patterns across all test files
- Net reduction of ~40 lines while improving maintainability
Add AnimationCurve and Quaternion fields required by Phase 6 stress tests.
@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Jan 7, 2026

Reviewer's Guide

Hardens the ManageScriptableObject Unity editor tool with path normalization, auto-growing arrays, extended SerializedProperty type support (including AnimationCurve and Quaternion), and a dry-run validation mode, while consolidating shared Unity test helpers and adding a large stress-test suite plus fixtures for ScriptableObject operations.

Sequence diagram for manage_scriptable_object modify with dry-run validation

sequenceDiagram
    actor Caller
    participant PythonTool as manage_scriptable_object_py
    participant UnityBridge as UnityMCPServer
    participant ManageTool as ManageScriptableObject
    participant SO as ScriptableObject

    Caller->>PythonTool: manage_scriptable_object(action="modify", target, patches, dry_run)
    PythonTool->>PythonTool: coerce_bool(dry_run)
    PythonTool->>UnityBridge: send request { action: modify, target, patches, dryRun }

    UnityBridge->>ManageTool: HandleModify(params)
    ManageTool->>ManageTool: TryResolveTarget(target)
    ManageTool->>SO: new SerializedObject(SO)
    ManageTool->>ManageTool: parse patches JArray
    ManageTool->>ManageTool: dryRun = params["dryRun"]

    alt dryRun == true
        ManageTool->>ManageTool: ValidatePatches(SO, patches)
        loop each patch
            ManageTool->>ManageTool: NormalizePropertyPath(propertyPath)
            alt op == array_resize
                ManageTool->>SO: FindProperty(arrayPath)
                ManageTool-->>ManageTool: validationResult(ok, message, currentSize)
            else op == set
                ManageTool->>SO: FindProperty(normalizedPath)
                alt property found
                    ManageTool-->>ManageTool: validationResult(ok=true, propertyType)
                else auto-growable array
                    ManageTool-->>ManageTool: validationResult(ok=true, will auto-grow)
                else not found
                    ManageTool-->>ManageTool: validationResult(ok=false, message)
                end
            end
        end
        ManageTool-->>UnityBridge: SuccessResponse { dryRun:true, valid, validationResults }
    else dryRun == false
        ManageTool->>ManageTool: ApplyPatches(SO, patches)
        loop each patch
            ManageTool->>ManageTool: ApplyPatch(SerializedObject, propertyPath, op, patch)
            ManageTool->>ManageTool: NormalizePropertyPath(propertyPath)
            alt op == array_resize
                ManageTool->>ManageTool: ApplyArrayResize
            else op == set
                ManageTool->>ManageTool: ApplySet (EnsureArrayCapacity, TrySetValueRecursive)
            end
        end
        ManageTool-->>UnityBridge: SuccessResponse { results, warnings }
    end

    UnityBridge-->>PythonTool: JSON response
    PythonTool-->>Caller: parsed response
Loading

File-Level Changes

Change Details Files
Add dry-run validation and friendlier, auto-growing property path handling to ManageScriptableObject modify operations.
  • Introduce a dryRun/dry_run parameter for modify actions that validates patches and returns per-patch results without mutating the asset.
  • Normalize friendly array paths like myList[5] into Unity’s .Array.data[5] format before resolution.
  • Add ValidatePatches to check property existence, array-resize semantics, and auto-growable array elements, returning structured ok/message/type metadata.
  • Ensure arrays are resized on demand via EnsureArrayCapacity when setting out-of-bounds indices and improve error messages when resolution fails.
MCPForUnity/Editor/Tools/ManageScriptableObject.cs
Server/src/services/tools/manage_scriptable_object.py
Server/tests/integration/test_manage_scriptable_object_tool.py
Extend SerializedProperty value setting to support bulk array/struct mapping and new types (AnimationCurve, Quaternion) using shared parsing helpers.
  • Refactor TrySetValue into a recursive setter that can map JArray to array/list properties and JObject to Generic (struct/class) properties field-by-field.
  • Replace local int/float/bool/vector/color parsing helpers with shared ParamCoercion and VectorParsing utilities, adding Vector4 parsing support.
  • Implement TrySetAnimationCurve to build AnimationCurve from JSON keyframe arrays or keyed objects, and TrySetQuaternion to parse multiple quaternion/euler representations.
  • Improve unsupported-type error messages with guidance on alternatives when a SerializedPropertyType cannot be patched.
MCPForUnity/Editor/Tools/ManageScriptableObject.cs
MCPForUnity/Editor/Helpers/VectorParsing.cs
Introduce shared EditMode test utilities and refactor existing Unity tests to use them for JSON conversion, folder management, shader selection, and cleanup.
  • Add TestUtilities with ToJObject, EnsureFolder, WaitForUnityReady, FindFallbackShader, SafeDeleteAsset, and CleanupEmptyParentFolders helpers.
  • Update multiple EditMode test suites to use TestUtilities via static imports, removing duplicated helper implementations and standardizing Temp folder cleanup.
  • Ensure tests rely on CleanupEmptyParentFolders instead of ad-hoc directory deletion logic to avoid lingering Assets/Temp artifacts.
TestProjects/UnityMCPTests/Assets/Tests/EditMode/TestUtilities.cs
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageScriptableObjectTests.cs
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageMaterialStressTests.cs
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageMaterialTests.cs
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManagePrefabsTests.cs
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/MaterialDirectPropertiesTests.cs
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/MaterialParameterToolTests.cs
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/GameObjectAPIStressTests.cs
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ReadConsoleTests.cs
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageMaterialPropertiesTests.cs
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageMaterialReproTests.cs
Add a comprehensive ManageScriptableObject stress-test suite and ScriptableObject fixtures to exercise nested arrays, deep structures, type coercion, and new features.
  • Create ComplexStressSO, ArrayStressSO, and DeepStressSO fixtures to cover large nested arrays, deep object graphs, and array boundary conditions.
  • Add ManageScriptableObjectStressTests with scenarios for big-bang list population, out-of-bounds array writes, friendly path syntax, deep nesting, mixed-type patches, rapid sequential modifies, type mismatch errors, bulk array mapping, GUID shorthand references, dry-run behavior, and extended AnimationCurve/Quaternion formats.
  • Wire stress tests to use shared TestUtilities for setup/teardown, asset folder management, and Unity compilation waits.
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageScriptableObjectStressTests.cs
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/Fixtures/StressTestSOs/ComplexStressSO.cs
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/Fixtures/StressTestSOs/ArrayStressSO.cs
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/Fixtures/StressTestSOs/DeepStressSO.cs

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 7, 2026

📝 Walkthrough

Walkthrough

This pull request introduces dry-run validation mode for the ManageScriptableObject tool, adds ParseVector4 support to vector parsing, implements path normalization for property access, enables automatic array capacity handling, creates new specialized setters for AnimationCurve and Quaternion types, and establishes centralized test utilities with comprehensive stress tests covering nested structures, array operations, and multiple data types.

Changes

Cohort / File(s) Summary
Vector Parsing Enhancement
MCPForUnity/Editor/Helpers/VectorParsing.cs
Adds ParseVector4(JToken token) method supporting array [x, y, z, w] and object {x, y, z, w} formats with null-safe error handling; integrates with existing Vector2/Vector3 patterns.
ManageScriptableObject Core Enhancement
MCPForUnity/Editor/Tools/ManageScriptableObject.cs
Introduces dry-run mode for patch validation without applying changes; adds path normalization (e.g., myList[5] → internal Array.data paths); implements array auto-resize via EnsureArrayCapacity; adds specialized setters for AnimationCurve (keyframe/compact formats) and Quaternion (Euler/component/object formats); extends validation with field/path/operation constraints; replaces legacy local helpers with shared ParamCoercion/VectorParsing utilities; adds recursive bulk array/object mapping with depth guards.
Python Service Integration
Server/src/services/tools/manage_scriptable_object.py
Server/tests/integration/test_manage_scriptable_object_tool.py
Adds dry_run parameter (bool/str/None) forwarded as dryRun in Unity command payload via coerce_bool. New tests verify parameter passing and string-to-boolean coercion.
Test Utilities Framework
TestProjects/UnityMCPTests/Assets/Tests/EditMode/TestUtilities.cs
TestProjects/UnityMCPTests/Assets/Tests/EditMode/TestUtilities.cs.meta
Introduces MCPForUnityTests.Editor.TestUtilities static helper class with methods: ToJObject() for result conversion, EnsureFolder() for asset folder creation, WaitForUnityReady() coroutine with timeout, FindFallbackShader() for pipeline-aware shader selection, SafeDeleteAsset(), and CleanupEmptyParentFolders() for recursive cleanup.
Stress Test Fixtures
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/Fixtures/StressTestSOs/*
Creates fixture ScriptableObjects: ArrayStressSO (float array), ComplexStressSO (basic types, arrays, lists, nested structs/classes, enums, AnimationCurve, Quaternion), DeepStressSO (3-level nested structures); includes corresponding .meta files.
Stress Test Suite
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageScriptableObjectStressTests.cs
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageScriptableObjectStressTests.cs.meta
Comprehensive test harness (~20 test methods) covering: large nested array creation, out-of-bounds access, path normalization validation, deep nesting, mixed references, rapid sequential patches, type mismatches, bulk array/object mapping, GUID shorthand assignment, dry-run validation, and Phase 6 features (AnimationCurve/Quaternion via multiple formats).
Test Utilities Migration
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/{GameObjectAPIStressTests,ManageMaterial\*Tests,ManagePrefabsTests,ManageScriptableObjectTests,MaterialDirectPropertiesTests,MaterialParameterToolTests,ReadConsoleTests}.cs
Refactors 10 existing test files: replaces local ToJObject() helpers with static import; replaces manual empty-folder cleanup with CleanupEmptyParentFolders() calls; removes local shader fallback chains where applicable.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

🐰 A vector four-th, now parsed with care,
Paths normalized through the editor's air,
Dry runs validate without a trace,
Arrays auto-grow to their rightful place,
Quaternions spin and curves animate,
Tests unified—utilities celebrate!

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 31.94% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'Harden manage_scriptable_object Tool' clearly and specifically describes the main objective of the changeset: improving robustness and capability of the ManageScriptableObject tool with path normalization, auto-resizing arrays, extended type support, dry-run validation, and test infrastructure improvements.

✏️ Tip: You can configure your own custom Pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @TestProjects/UnityMCPTests/Assets/Tests/EditMode/TestUtilities.cs:
- Around line 113-130: The catch block around
Directory.GetDirectories/Directory.GetFiles/AssetDatabase.DeleteAsset silently
swallows exceptions; update the catch in the cleanup loop to at least log the
caught exception (e.g., using UnityEngine.Debug.LogException or Debug.LogWarning
with context like the current parent path) and then break/return as appropriate
so failures aren't hidden—locate the try/catch that references parent,
Directory.GetDirectories, Directory.GetFiles and AssetDatabase.DeleteAsset and
replace the empty catch with logging of the exception and a clear break.
🧹 Nitpick comments (5)
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/Fixtures/StressTestSOs/ComplexStressSO.cs (1)

17-17: Consider initializing the scores list.

List<float> scores will be null by default. If stress tests access this field before initialization, it could cause NullReferenceException. Unity's serialization will handle this during asset creation, but explicit initialization can make the fixture more robust.

🔎 Optional: Initialize scores list
 public class ComplexSubClass
 {
     public string name;
     public int level;
-    public List<float> scores;
+    public List<float> scores = new List<float>();
 }
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/Fixtures/StressTestSOs/ArrayStressSO.cs (1)

1-7: Consider adding a namespace declaration.

While this test fixture follows the same pattern as other stress test ScriptableObjects (DeepStressSO, ComplexStressSO), it lacks a namespace declaration. Adding a namespace like MCPForUnityTests.Editor.Tools.Fixtures.StressTestSOs would follow C# best practices and reduce the risk of type name collisions.

🔎 Proposed refactor
 using UnityEngine;
+
+namespace MCPForUnityTests.Editor.Tools.Fixtures.StressTestSOs
+{
+    [CreateAssetMenu(fileName = "ArrayStressSO", menuName = "StressTests/ArrayStressSO")]
+    public class ArrayStressSO : ScriptableObject
+    {
+        public float[] floatArray = new float[3];
+    }
+}
-
-[CreateAssetMenu(fileName = "ArrayStressSO", menuName = "StressTests/ArrayStressSO")]
-public class ArrayStressSO : ScriptableObject
-{
-    public float[] floatArray = new float[3];
-}
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/Fixtures/StressTestSOs/DeepStressSO.cs (1)

2-2: Unused import.

System.Collections.Generic is not used in this file.

🔎 Proposed fix
 using UnityEngine;
-using System.Collections.Generic;
MCPForUnity/Editor/Tools/ManageScriptableObject.cs (1)

230-248: Using reflection to check anonymous type property is fragile.

Line 244 uses reflection (GetProperty("ok")?.GetValue(r)) to access the ok property of anonymous result objects. This works but is fragile—property name typos or refactoring could silently break validation. Consider using a named result type or LINQ with pattern matching.

🔎 Proposed fix
-                    valid = validationResults.All(r => (bool)r.GetType().GetProperty("ok")?.GetValue(r)),
+                    valid = validationResults.All(r => r is { } obj && 
+                        obj.GetType().GetProperty("ok")?.GetValue(obj) is bool ok && ok),

Or define a simple record/class for validation results to avoid reflection entirely.

TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageScriptableObjectStressTests.cs (1)

1082-1095: Test does not exercise any code paths.

This test immediately calls Assert.Pass() without actually verifying unsupported type behavior. Consider either implementing a real test or removing this placeholder.

🔎 Proposed fix

Either implement a proper test using a fixture with an unsupported type, or convert to a code comment and remove the empty test:

-        /// <summary>
-        /// Test: Unsupported type returns a helpful error message.
-        /// </summary>
-        [UnityTest]
-        public IEnumerator UnsupportedType_ReturnsHelpfulError()
-        {
-            yield return WaitForUnityReady();
-
-            // This test verifies that the improved error message is returned
-            // We can't easily test an actual unsupported type without creating a custom SO,
-            // so we just verify the error message format by checking the code path exists.
-            // The actual unsupported type behavior is implicitly tested if we ever add
-            // a field that hits the default case.
-
-            Debug.Log("[UnsupportedType] Error message improvement verified in code review");
-            Assert.Pass("Error message improvement verified in code");
-        }
+        // NOTE: Unsupported type error messaging is verified via code review.
+        // A dedicated test would require a custom SO with a field of an unsupported SerializedPropertyType.
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3090a01 and 25781d2.

📒 Files selected for processing (25)
  • MCPForUnity/Editor/Helpers/VectorParsing.cs
  • MCPForUnity/Editor/Tools/ManageScriptableObject.cs
  • Server/src/services/tools/manage_scriptable_object.py
  • Server/tests/integration/test_manage_scriptable_object_tool.py
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/TestUtilities.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/TestUtilities.cs.meta
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/Fixtures/StressTestSOs.meta
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/Fixtures/StressTestSOs/ArrayStressSO.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/Fixtures/StressTestSOs/ArrayStressSO.cs.meta
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/Fixtures/StressTestSOs/ComplexStressSO.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/Fixtures/StressTestSOs/ComplexStressSO.cs.meta
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/Fixtures/StressTestSOs/DeepStressSO.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/Fixtures/StressTestSOs/DeepStressSO.cs.meta
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/GameObjectAPIStressTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageMaterialPropertiesTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageMaterialReproTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageMaterialStressTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageMaterialTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManagePrefabsTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageScriptableObjectStressTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageScriptableObjectStressTests.cs.meta
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageScriptableObjectTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/MaterialDirectPropertiesTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/MaterialParameterToolTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ReadConsoleTests.cs
🧰 Additional context used
🧠 Learnings (5)
📚 Learning: 2025-12-29T15:23:11.613Z
Learnt from: msanatan
Repo: CoplayDev/unity-mcp PR: 491
File: MCPForUnity/Editor/Windows/EditorPrefs/EditorPrefsWindow.cs:78-115
Timestamp: 2025-12-29T15:23:11.613Z
Learning: In MCPForUnity, prefer relying on the established testing process to catch UI initialization issues instead of adding defensive null checks for UI elements in editor windows. This means during reviews, verify that tests cover UI initialization paths and that code avoids repetitive null-check boilerplate in Editor Windows. If a UI element can be null, ensure there is a well-tested fallback or that its initialization is guaranteed by design, rather than sprinkling null checks throughout editor code.

Applied to files:

  • MCPForUnity/Editor/Helpers/VectorParsing.cs
  • MCPForUnity/Editor/Tools/ManageScriptableObject.cs
📚 Learning: 2025-09-03T16:00:55.839Z
Learnt from: dsarno
Repo: CoplayDev/unity-mcp PR: 0
File: :0-0
Timestamp: 2025-09-03T16:00:55.839Z
Learning: ComponentResolver in UnityMcpBridge/Editor/Tools/ManageGameObject.cs is a nested static class within ManageGameObject, not a sibling type. The `using static MCPForUnity.Editor.Tools.ManageGameObject;` import is required to access ComponentResolver methods directly without the outer class qualifier.

Applied to files:

  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManagePrefabsTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageMaterialStressTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ReadConsoleTests.cs
  • MCPForUnity/Editor/Tools/ManageScriptableObject.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/MaterialParameterToolTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/TestUtilities.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageScriptableObjectTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageMaterialPropertiesTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageScriptableObjectStressTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageMaterialTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/GameObjectAPIStressTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/MaterialDirectPropertiesTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageMaterialReproTests.cs
📚 Learning: 2025-12-29T15:23:17.871Z
Learnt from: msanatan
Repo: CoplayDev/unity-mcp PR: 491
File: MCPForUnity/Editor/Windows/EditorPrefs/EditorPrefsWindow.cs:78-115
Timestamp: 2025-12-29T15:23:17.871Z
Learning: In the MCPForUnity project, the team prefers to rely on their testing process to catch UI initialization issues rather than adding defensive null checks for UI elements in editor windows.

Applied to files:

  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManagePrefabsTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageMaterialStressTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ReadConsoleTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/MaterialParameterToolTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/TestUtilities.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageScriptableObjectTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageMaterialPropertiesTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageScriptableObjectStressTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageMaterialTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/GameObjectAPIStressTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/MaterialDirectPropertiesTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageMaterialReproTests.cs
📚 Learning: 2025-10-13T13:27:23.040Z
Learnt from: msanatan
Repo: CoplayDev/unity-mcp PR: 316
File: TestProjects/UnityMCPTests/Assets/Tests/EditMode/Resources.meta:1-8
Timestamp: 2025-10-13T13:27:23.040Z
Learning: UnityMcpBridge is a legacy project kept for backwards compatibility; MCPForUnity is the only active Unity plugin project. GUID collisions between UnityMcpBridge and MCPForUnity are acceptable.

Applied to files:

  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManagePrefabsTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageMaterialStressTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ReadConsoleTests.cs
  • MCPForUnity/Editor/Tools/ManageScriptableObject.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/MaterialParameterToolTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/TestUtilities.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageScriptableObjectTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageMaterialPropertiesTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageMaterialTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/GameObjectAPIStressTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/MaterialDirectPropertiesTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageMaterialReproTests.cs
📚 Learning: 2025-10-13T13:41:00.086Z
Learnt from: JohanHoltby
Repo: CoplayDev/unity-mcp PR: 309
File: MCPForUnity/Editor/Helpers/ServerInstaller.cs:478-508
Timestamp: 2025-10-13T13:41:00.086Z
Learning: In the MCPForUnityTools feature (MCPForUnity/Editor/Helpers/ServerInstaller.cs), the design intentionally forces users to have only one .py file per MCPForUnityTools folder to keep file tracking simple. Package-style tools (subdirectories with __init__.py) are not supported.

Applied to files:

  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManagePrefabsTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageMaterialStressTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ReadConsoleTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/MaterialParameterToolTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageScriptableObjectTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageMaterialPropertiesTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageMaterialTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/GameObjectAPIStressTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/MaterialDirectPropertiesTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageMaterialReproTests.cs
🧬 Code graph analysis (9)
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/Fixtures/StressTestSOs/ArrayStressSO.cs (2)
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/Fixtures/StressTestSOs/ComplexStressSO.cs (1)
  • CreateAssetMenu (27-52)
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/Fixtures/StressTestSOs/DeepStressSO.cs (1)
  • CreateAssetMenu (25-30)
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManagePrefabsTests.cs (1)
TestProjects/UnityMCPTests/Assets/Tests/EditMode/TestUtilities.cs (2)
  • TestUtilities (15-138)
  • CleanupEmptyParentFolders (103-137)
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageMaterialStressTests.cs (1)
TestProjects/UnityMCPTests/Assets/Tests/EditMode/TestUtilities.cs (2)
  • TestUtilities (15-138)
  • CleanupEmptyParentFolders (103-137)
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ReadConsoleTests.cs (1)
TestProjects/UnityMCPTests/Assets/Tests/EditMode/TestUtilities.cs (1)
  • TestUtilities (15-138)
Server/tests/integration/test_manage_scriptable_object_tool.py (2)
Server/tests/integration/test_helpers.py (2)
  • DummyContext (16-55)
  • set_state (49-51)
Server/src/services/tools/manage_scriptable_object.py (1)
  • manage_scriptable_object (32-80)
TestProjects/UnityMCPTests/Assets/Tests/EditMode/TestUtilities.cs (2)
MCPForUnity/Editor/Helpers/AssetPathUtility.cs (2)
  • AssetPathUtility (15-212)
  • SanitizeAssetPath (31-45)
MCPForUnity/Editor/Tools/ManageAsset.cs (2)
  • CreateFolder (279-332)
  • DeleteAsset (495-523)
Server/src/services/tools/manage_scriptable_object.py (1)
Server/src/services/tools/utils.py (1)
  • coerce_bool (11-24)
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageScriptableObjectStressTests.cs (1)
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/Fixtures/ManageScriptableObjectTestDefinition.cs (1)
  • ManageScriptableObjectTestDefinition (14-23)
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/GameObjectAPIStressTests.cs (1)
TestProjects/UnityMCPTests/Assets/Tests/EditMode/TestUtilities.cs (1)
  • TestUtilities (15-138)
🪛 Ruff (0.14.10)
Server/tests/integration/test_manage_scriptable_object_tool.py

75-75: Unused function argument: kwargs

(ARG001)


106-106: Unused function argument: kwargs

(ARG001)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Sourcery review
🔇 Additional comments (46)
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/Fixtures/StressTestSOs.meta (1)

1-8: LGTM!

This is a standard Unity folder metadata file. The GUID is valid, the folderAsset flag is correctly set, and the structure conforms to Unity conventions for asset metadata.

TestProjects/UnityMCPTests/Assets/Tests/EditMode/TestUtilities.cs.meta (1)

1-11: Meta file structure is correct.

The Unity meta file follows standard conventions with a valid GUID and proper MonoImporter configuration for the TestUtilities.cs script.

TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageScriptableObjectStressTests.cs.meta (1)

1-11: Meta file structure is correct.

The Unity meta file follows standard conventions with a valid GUID and proper MonoImporter configuration for the stress test script.

TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/Fixtures/StressTestSOs/DeepStressSO.cs.meta (1)

1-11: Meta file structure is correct.

The Unity meta file follows standard conventions with a valid GUID and proper MonoImporter configuration for the fixture script.

TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/GameObjectAPIStressTests.cs (1)

12-12: LGTM! Clean refactor to shared test utility.

The switch from a local ToJObject helper to the shared TestUtilities.ToJObject via static import reduces duplication and aligns with the test infrastructure consolidation across the PR.

Server/src/services/tools/manage_scriptable_object.py (2)

44-45: LGTM! Clean addition of dry_run parameter.

The parameter follows the established pattern (matching overwrite), accepts both bool and string for LLM flexibility, and uses coerce_bool consistently. The "(modify only)" constraint in the docstring matches the PR objectives.


67-67: Consistent forwarding to Unity.

The dryRun key is correctly forwarded with coerce_bool coercion and will be filtered out when None (line 71), keeping the Unity handler interface clean.

Server/tests/integration/test_manage_scriptable_object_tool.py (3)

72-100: Good test coverage for dry_run forwarding.

The test properly verifies that dry_run=True is forwarded as dryRun=True in the Unity command payload, along with correct handling of target and patches.


102-128: Thorough string coercion test.

Validates that string "true" is correctly coerced to boolean True, which is important for LLM-generated inputs that may stringify boolean values.


75-78: The **kwargs is intentional for signature compatibility.

Ruff flags unused kwargs (ARG001), but this is a false positive. The **kwargs is required to match the signature of async_send_command_with_retry being monkeypatched. This pattern is consistent with the existing tests at lines 10-13 and 46-49.

Also applies to: 106-109

TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/Fixtures/StressTestSOs/ComplexStressSO.cs (1)

1-52: Well-structured stress test fixture.

The ComplexStressSO provides comprehensive field coverage for testing the hardened ManageScriptableObject tool:

  • Basic types (int, float, string, bool, Vector3, Color, enum)
  • Collections (arrays and lists)
  • Nested types (struct and class)
  • Extended types (AnimationCurve, Quaternion)

This aligns well with the PR's goals of testing path normalization, auto-resizing arrays, and extended type support.

TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/Fixtures/StressTestSOs/ComplexStressSO.cs.meta (1)

1-11: Standard Unity meta file.

The metadata is correctly structured with a unique GUID and default MonoImporter settings.

TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/Fixtures/StressTestSOs/ArrayStressSO.cs.meta (1)

1-11: Standard Unity meta file.

The metadata is correctly structured for the ArrayStressSO test fixture.

TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ReadConsoleTests.cs (1)

6-6: LGTM! Consistent refactor to shared test utilities.

The static import of TestUtilities aligns with the PR's consolidation of test helpers across 11 test files, reducing code duplication.

TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManagePrefabsTests.cs (1)

8-8: LGTM! Clean refactoring to centralized test utilities.

The static import and shared cleanup logic eliminate code duplication across the test suite. The CleanupEmptyParentFolders call correctly handles empty parent directories after asset deletion.

Also applies to: 34-35

TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageMaterialReproTests.cs (1)

7-7: LGTM! Consistent refactoring to shared test utilities.

The changes align with the test infrastructure improvements across the suite, reducing duplication and improving maintainability.

Also applies to: 40-41

TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageMaterialStressTests.cs (1)

8-8: LGTM! Test utility consolidation.

Consistent with the refactoring pattern applied across the test suite. The centralized utilities improve maintainability and reduce duplication.

Also applies to: 55-56

MCPForUnity/Editor/Helpers/VectorParsing.cs (1)

99-140: LGTM! ParseVector4 follows established patterns.

The implementation is consistent with ParseVector2 and ParseVector3, supporting both array [x,y,z,w] and object {x,y,z,w} formats. Error handling and documentation align with existing methods in this class.

TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageMaterialPropertiesTests.cs (1)

7-7: LGTM! Consolidation to centralized test utilities.

The refactoring reduces duplication and aligns with the test infrastructure improvements across the PR.

Also applies to: 38-39

TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageScriptableObjectTests.cs (1)

10-10: LGTM! Test utilities consolidation improves maintainability.

The refactor to use shared TestUtilities (FindFallbackShader, CleanupEmptyParentFolders) reduces duplication and centralizes common test helpers. The static import enables clean, unqualified method calls throughout the test file.

Also applies to: 44-44, 75-76

TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/MaterialDirectPropertiesTests.cs (1)

8-8: LGTM! Consistent test utilities refactor.

The addition of the static TestUtilities import and the switch to CleanupEmptyParentFolders aligns with the broader test suite refactor, reducing duplication and improving maintainability.

Also applies to: 61-62

TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageMaterialTests.cs (1)

8-8: LGTM! Consistent test utilities refactor.

The changes mirror the pattern used across the test suite—consolidating cleanup and helper methods into shared TestUtilities for better maintainability.

Also applies to: 46-47

TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/MaterialParameterToolTests.cs (1)

8-8: LGTM! Consistent test utilities refactor.

The refactor to use shared TestUtilities and centralized cleanup logic is consistent with the other test files in this PR, improving code reuse and reducing duplication.

Also applies to: 66-67

TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/Fixtures/StressTestSOs/DeepStressSO.cs (1)

4-30: Well-structured test fixture for deep nesting scenarios.

The nested Level1 → Level2 → Level3 hierarchy with mixed types (string, Vector3, Color) provides good coverage for the path traversal and deep nesting stress tests.

TestProjects/UnityMCPTests/Assets/Tests/EditMode/TestUtilities.cs (3)

21-25: LGTM!

Clean null-safe conversion with appropriate fallback to JObject.FromObject for non-JObject results.


32-52: LGTM!

Properly delegates path sanitization to AssetPathUtility.SanitizeAssetPath and iteratively creates parent folders. This aligns with the pattern used in ManageAsset.cs.


60-71: LGTM!

Appropriate timeout mechanism with Assert.Fail for test failures when Unity is stuck compiling/updating.

MCPForUnity/Editor/Tools/ManageScriptableObject.cs (12)

265-405: Dry-run validation logic is well-structured.

The ValidatePatches method correctly:

  • Normalizes paths before validation
  • Checks array existence and type for array_resize
  • Detects auto-growable array paths
  • Returns detailed validation results with property type info

This provides useful feedback without mutating assets.


485-521: Path normalization regex handles edge cases correctly.

The logic to avoid double-conversion (checking if already in .Array.data[n] format) is sound. The bounds check at line 509 (matchStart >= 7) correctly ensures we can safely look back 7 characters for .Array..


523-566: Auto-resize array capacity handling is robust.

EnsureArrayCapacity correctly:

  • Parses the array path and target index
  • Validates the array property exists and is actually an array
  • Grows the array and applies modifications immediately

The immediate ApplyModifiedProperties() + Update() pattern ensures subsequent property lookups succeed.


662-702: LGTM!

Good error messaging when array auto-grow fails—provides specific context about whether the array itself wasn't found or isn't actually an array.


706-765: Enhanced reference resolution with multiple formats.

Good addition of GUID shorthand (32-char hex string) and path shorthand detection. The resolveMethod tracking in the response message helps with debugging reference resolution issues.


790-846: Bulk array mapping with recursion depth guard.

The MaxRecursionDepth = 20 guard prevents stack overflow on circular references. The partial success handling (returning true if successCount > 0) is reasonable for bulk operations.


848-886: Bulk object mapping for nested structs.

Recursively setting fields via JObject key-value pairs with proper child path construction (prop.propertyPath + "." + kvp.Key) is clean. Partial success semantics match the array handling above.


892-942: Improved type coercion using shared helpers.

Using ParamCoercion.CoerceInt/Float/Bool provides consistent parsing across the codebase. The additional validation for boolean (checking for recognized string values) is a good safeguard.


1000-1004: Extended type support for AnimationCurve and Quaternion.

Good addition of these commonly-needed Unity types to the switch statement, delegating to specialized setters.


1058-1143: Flexible AnimationCurve input formats.

Accepting both { "keys": [...] } and direct array [...] provides good DX. Support for optional weighted tangent properties (weightedMode, inWeight, outWeight) covers advanced use cases.


1145-1240: Comprehensive Quaternion input format support.

Four input formats covered:

  • [x, y, z] as Euler (degrees)
  • [x, y, z, w] as raw components
  • { x, y, z, w } as object
  • { euler: [x, y, z] } for explicit Euler

Clear error messages guide users to valid formats.


1019-1023: Helpful error message for unsupported types.

The extended error message with actionable suggestions ("Consider editing the .asset file directly...") is user-friendly.

TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageScriptableObjectStressTests.cs (7)

34-61: Well-structured test setup with proper isolation.

Using Guid.NewGuid() for unique run directories ensures test isolation. Creating test assets (Material, Texture2D) for reference tests is appropriate setup.


63-84: Thorough teardown with asset cleanup.

Properly iterates _createdAssets, deletes the run root folder, and uses CleanupEmptyParentFolders to avoid debris. This prevents test pollution.


88-159: Comprehensive big-bang stress test.

Testing 50 elements with 151 patches (1 resize + 50×3 field sets) and verifying both first and last elements provides good coverage for bulk operations.


165-209: Good documentation of expected behavior.

The comments on lines 199-201 and 207-208 clearly document current vs. expected behavior, which is valuable for understanding test intent during hardening.


671-732: Dry-run test validates non-mutation correctly.

Key assertion at line 729 (Assert.AreEqual(originalValue, asset.intValue, ...)) verifies that dry-run truly doesn't modify the asset. Testing both valid and invalid patches in one call is efficient.


742-800: Comprehensive AnimationCurve test with slope verification.

Good coverage of the keyframe structure including inSlope/outSlope values. Verifying key count and individual time/value assertions confirms correct parsing.


858-906: Quaternion Euler angle test with precision verification.

Converting expected Euler to Quaternion and comparing components with tolerance (0.001f) is the correct approach for floating-point quaternion comparisons.

Comment on lines +113 to +130
try
{
var dirs = Directory.GetDirectories(parent);
var files = Directory.GetFiles(parent);
if (dirs.Length == 0 && files.Length == 0)
{
AssetDatabase.DeleteAsset(parent);
parent = Path.GetDirectoryName(parent)?.Replace('\\', '/');
}
else
{
break;
}
}
catch
{
break;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Empty catch block silently swallows exceptions.

Consider logging or at minimum breaking out with a comment explaining why the exception is intentionally ignored. Silent exception swallowing can mask real issues during test cleanup.

🔎 Proposed fix
                     catch
                     {
+                        // Cleanup is best-effort; ignore errors (e.g., file locks, permissions)
                         break;
                     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
try
{
var dirs = Directory.GetDirectories(parent);
var files = Directory.GetFiles(parent);
if (dirs.Length == 0 && files.Length == 0)
{
AssetDatabase.DeleteAsset(parent);
parent = Path.GetDirectoryName(parent)?.Replace('\\', '/');
}
else
{
break;
}
}
catch
{
break;
}
try
{
var dirs = Directory.GetDirectories(parent);
var files = Directory.GetFiles(parent);
if (dirs.Length == 0 && files.Length == 0)
{
AssetDatabase.DeleteAsset(parent);
parent = Path.GetDirectoryName(parent)?.Replace('\\', '/');
}
else
{
break;
}
}
catch
{
// Cleanup is best-effort; ignore errors (e.g., file locks, permissions)
break;
}
🤖 Prompt for AI Agents
In @TestProjects/UnityMCPTests/Assets/Tests/EditMode/TestUtilities.cs around
lines 113 - 130, The catch block around
Directory.GetDirectories/Directory.GetFiles/AssetDatabase.DeleteAsset silently
swallows exceptions; update the catch in the cleanup loop to at least log the
caught exception (e.g., using UnityEngine.Debug.LogException or Debug.LogWarning
with context like the current parent path) and then break/return as appropriate
so failures aren't hidden—locate the try/catch that references parent,
Directory.GetDirectories, Directory.GetFiles and AssetDatabase.DeleteAsset and
replace the empty catch with logging of the exception and a clear break.

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 3 issues, and left some high level feedback:

  • In ValidatePatches, you’re building anonymous result objects and then using reflection to read the ok property for the overall valid flag; consider introducing a small DTO class/struct for validation results so you can avoid reflection and make the shape of the response explicit and easier to evolve.
  • NormalizePropertyPath and EnsureArrayCapacity both rely on regexes that will be hit on every patch; given the new stress workloads, it may be worth hoisting these to static readonly Regex instances (or sharing a single compiled pattern) to reduce allocations and improve performance under heavy use.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `ValidatePatches`, you’re building anonymous result objects and then using reflection to read the `ok` property for the overall `valid` flag; consider introducing a small DTO class/struct for validation results so you can avoid reflection and make the shape of the response explicit and easier to evolve.
- `NormalizePropertyPath` and `EnsureArrayCapacity` both rely on regexes that will be hit on every patch; given the new stress workloads, it may be worth hoisting these to `static readonly Regex` instances (or sharing a single compiled pattern) to reduce allocations and improve performance under heavy use.

## Individual Comments

### Comment 1
<location> `MCPForUnity/Editor/Tools/ManageScriptableObject.cs:244` </location>
<code_context>
+                        targetPath,
+                        targetTypeName = target.GetType().FullName,
+                        dryRun = true,
+                        valid = validationResults.All(r => (bool)r.GetType().GetProperty("ok")?.GetValue(r)),
+                        validationResults
+                    }
</code_context>

<issue_to_address>
**suggestion:** Avoid using reflection to compute `valid` for dry-run results to reduce brittleness and overhead.

Since `validationResults` is built entirely within `ValidatePatches`, you already control and know the shape of each result, including the `ok` flag. Using reflection here means a future rename or omission of `ok` will only fail at runtime and may be hard to detect, and it adds overhead on a potentially hot path if many patches are processed. Prefer a strongly typed result (e.g., a small struct/class with an `Ok` property) or track the `ok` flag alongside the list so `valid` can be computed directly without reflection.

Suggested implementation:

```csharp
                var (validationResults, isValid) = ValidatePatches(target, patches);
                return new SuccessResponse(
                    "Dry-run validation complete.",
                    new
                    {
                        targetGuid,
                        targetPath,
                        targetTypeName = target.GetType().FullName,
                        dryRun = true,
                        valid = isValid,
                        validationResults
                    }

```

To fully remove the reflection and make this compile, you also need to:

1. Update the `ValidatePatches` signature to return both the collection of validation results and an aggregate validity flag, for example:
   - From:  
     `private static IList<ValidationResult> ValidatePatches(object target, IEnumerable<Patch> patches)`
   - To (example):  
     `private static (IReadOnlyList<ValidationResult> Results, bool IsValid) ValidatePatches(object target, IEnumerable<Patch> patches)`

2. Inside `ValidatePatches`, compute `IsValid` directly from the strongly-typed results, e.g.:
   ```csharp
   var results = collectedResults.ToArray();
   var isValid = results.All(r => r.Ok);
   return (results, isValid);
   ```
   where `ValidationResult` is a concrete type you control with a boolean `Ok` (or similarly named) property.

3. Adjust any other call sites of `ValidatePatches` to use the new return type, either by:
   ```csharp
   var (validationResults, isValid) = ValidatePatches(target, patches);
   ```
   or, if they only care about the list:
   ```csharp
   var (validationResults, _) = ValidatePatches(target, patches);
   ```
</issue_to_address>

### Comment 2
<location> `TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageScriptableObjectStressTests.cs:88-97` </location>
<code_context>
-
         #region Bulk GameObject Creation

         [Test]
</code_context>

<issue_to_address>
**issue (testing):** This out-of-bounds test documents failure, but the implementation now auto-resizes arrays, so the assertions should be updated to check for success and the new array size.

The `EnsureArrayCapacity` helper now grows arrays when setting an element beyond current bounds, so this tests expectation of failure will no longer be correct.

Please update the test to reflect the new behavior by renaming it to something like `OutOfBounds_SetElementBeyondArraySize_AutoGrowsArray` and explicitly asserting that:
- `ok == true` in the patch result
- `floatArray` has grown to at least length 100
- index `99` is set to `42.0f`

This will validate the hardening behavior instead of just logging the old failure case.
</issue_to_address>

### Comment 3
<location> `TestProjects/UnityMCPTests/Assets/Tests/EditMode/TestUtilities.cs:21-23` </location>
<code_context>
+        /// Safely converts a command result to JObject, handling both JSON objects and other types.
+        /// Returns an empty JObject if result is null.
+        /// </summary>
+        public static JObject ToJObject(object result)
+        {
+            if (result == null) return new JObject();
+            return result as JObject ?? JObject.FromObject(result);
+        }
</code_context>

<issue_to_address>
**suggestion (testing):** Returning an empty JObject on null results may hide bugs that earlier per-test helpers caught; consider providing an assertion-based variant or using this helper carefully in tests that expect non-null results.

Previously, local `ToJObject` helpers in tests (e.g., `ReadConsoleTests`) would `Assert.Fail` on null results. The shared `TestUtilities.ToJObject` now returns an empty `JObject` instead, which can mask cases where a non-null response is required.

For tests where a null result should fail, consider either:
- Adding a `ToNonNullJObject` helper that asserts on null and using it there, or
- Explicitly asserting `result is not null` before calling `ToJObject`.

This preserves the convenience of the shared helper while keeping tests from treating null results as valid empty payloads.

Suggested implementation:

```csharp
        /// <summary>
        /// Safely converts a command result to JObject, handling both JSON objects and other types.
        /// Returns an empty JObject if result is null.
        /// </summary>
        public static JObject ToJObject(object result)
        {
            if (result == null) return new JObject();
            return result as JObject ?? JObject.FromObject(result);
        }

        /// <summary>
        /// Converts a command result to JObject, asserting that the result is non-null.
        /// Use this in tests where a null result should cause the test to fail.
        /// </summary>
        /// <param name="result">The command result to convert. Must not be null.</param>
        /// <param name="message">Optional assertion failure message.</param>
        public static JObject ToNonNullJObject(object result, string message = null)
        {
            Assert.IsNotNull(result, message ?? "Expected non-null result when converting to JObject.");
            return ToJObject(result);
        }

        /// <summary>
        /// Creates all parent directories for the given asset path if they don't exist.
        /// Handles normalization and validates against dangerous patterns.
        /// </summary>

```

1. Ensure that `TestUtilities.cs` has `using NUnit.Framework;` (or the appropriate assertion library) at the top of the file so that `Assert.IsNotNull` is available.
2. Update tests that previously relied on null causing failures (e.g., those that had local `ToJObject` helpers with `Assert.Fail`) to call `TestUtilities.ToNonNullJObject(...)` instead of `ToJObject(...)` where a non-null result is required.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +88 to +97
[Test]
public void BigBang_CreateWithLargeNestedArray()
{
// Create a ComplexStressSO with a large nestedDataList in one create call
const int elementCount = 50; // Start moderate, can increase after hardening

var patches = new JArray();

// First resize the array
patches.Add(new JObject
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (testing): This out-of-bounds test documents failure, but the implementation now auto-resizes arrays, so the assertions should be updated to check for success and the new array size.

The EnsureArrayCapacity helper now grows arrays when setting an element beyond current bounds, so this test’s expectation of failure will no longer be correct.

Please update the test to reflect the new behavior by renaming it to something like OutOfBounds_SetElementBeyondArraySize_AutoGrowsArray and explicitly asserting that:

  • ok == true in the patch result
  • floatArray has grown to at least length 100
  • index 99 is set to 42.0f

This will validate the hardening behavior instead of just logging the old failure case.

Comment on lines +21 to +23
public static JObject ToJObject(object result)
{
if (result == null) return new JObject();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (testing): Returning an empty JObject on null results may hide bugs that earlier per-test helpers caught; consider providing an assertion-based variant or using this helper carefully in tests that expect non-null results.

Previously, local ToJObject helpers in tests (e.g., ReadConsoleTests) would Assert.Fail on null results. The shared TestUtilities.ToJObject now returns an empty JObject instead, which can mask cases where a non-null response is required.

For tests where a null result should fail, consider either:

  • Adding a ToNonNullJObject helper that asserts on null and using it there, or
  • Explicitly asserting result is not null before calling ToJObject.

This preserves the convenience of the shared helper while keeping tests from treating null results as valid empty payloads.

Suggested implementation:

        /// <summary>
        /// Safely converts a command result to JObject, handling both JSON objects and other types.
        /// Returns an empty JObject if result is null.
        /// </summary>
        public static JObject ToJObject(object result)
        {
            if (result == null) return new JObject();
            return result as JObject ?? JObject.FromObject(result);
        }

        /// <summary>
        /// Converts a command result to JObject, asserting that the result is non-null.
        /// Use this in tests where a null result should cause the test to fail.
        /// </summary>
        /// <param name="result">The command result to convert. Must not be null.</param>
        /// <param name="message">Optional assertion failure message.</param>
        public static JObject ToNonNullJObject(object result, string message = null)
        {
            Assert.IsNotNull(result, message ?? "Expected non-null result when converting to JObject.");
            return ToJObject(result);
        }

        /// <summary>
        /// Creates all parent directories for the given asset path if they don't exist.
        /// Handles normalization and validates against dangerous patterns.
        /// </summary>
  1. Ensure that TestUtilities.cs has using NUnit.Framework; (or the appropriate assertion library) at the top of the file so that Assert.IsNotNull is available.
  2. Update tests that previously relied on null causing failures (e.g., those that had local ToJObject helpers with Assert.Fail) to call TestUtilities.ToNonNullJObject(...) instead of ToJObject(...) where a non-null result is required.

@msanatan msanatan merged commit 552b2d3 into CoplayDev:main Jan 7, 2026
2 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Jan 7, 2026
@dsarno dsarno deleted the feature/harden-scriptable-object-tool branch January 15, 2026 16:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants