Skip to content

Conversation

msanatan
Copy link
Contributor

@msanatan msanatan commented Sep 30, 2025

Summary by CodeRabbit

  • New Features

    • Expanded GameObject management: prefab-aware operations, advanced search methods (by id/name/path/tag/layer/component), and AI-backed property suggestions.
    • New includeNonPublicSerialized option threaded through component/property actions.
    • Added FindObjectByInstruction for more flexible object targeting.
  • Improvements

    • More robust component serialization and property conversion, with safer reflection and standardized responses.
    • Unity bridge stability: clearer logging, better handshake handling, structured timeout/error responses, and improved disconnect behavior.
  • Telemetry

    • Tool execution events can now include optional error details.

@msanatan msanatan self-assigned this Sep 30, 2025
Copy link
Contributor

coderabbitai bot commented Sep 30, 2025

Walkthrough

Adds skip-marking logic to a JUnit XML post-processor, significant refactors to Unity editor tooling (ManageGameObject, GameObjectSerializer, MCP bridge), and an optional error field to telemetry. Reorganizes a complex CI workflow (claude-nl-suite) around licensing gates. Broad formatting cleanups across C#, Python, and tests, plus minor workflow whitespace fixes.

Changes

Cohort / File(s) Summary
JUnit skip post-processor
.github/scripts/mark_skipped.py
New functions should_skip, summarize_counts, main; converts matching failed testcases to skipped, updates suite tallies, rewrites XML when changed.
NL-suite workflow rework
.github/workflows/claude-nl-suite.yml
Reorganized licensing (ULF/EBL) gates, activation, Unity bridge warm-up/health checks, consolidated reporting, retry/backfill for T tests, clearer orchestration and artifacts.
Other workflows (cosmetic)
.github/workflows/bump-version.yml, .github/workflows/github-repo-stats.yml, .github/workflows/unity-tests.yml
Whitespace/formatting only; no functional changes.
Unity Bridge core logic
UnityMcpBridge/Editor/Tools/ManageGameObject.cs, UnityMcpBridge/Editor/Helpers/GameObjectSerializer.cs, UnityMcpBridge/Editor/MCPForUnityBridge.cs, UnityMcpBridge/Editor/Helpers/TelemetryHelper.cs
ManageGameObject: expanded commands, prefab routing, robust create/find/set flows, new public FindObjectByInstruction, threaded includeNonPublicSerialized. GameObjectSerializer: expanded reflection/serialization, default param for non-public fields, caching, special cases. MCP bridge: sturdier connection/command handling and responses. Telemetry: RecordToolExecution adds optional error parameter.
Unity Editor windows/UI
UnityMcpBridge/Editor/Windows/MCPForUnityEditorWindow.cs, UnityMcpBridge/Editor/Windows/VSCodeManualSetupWindow.cs
Internal UI flow refinements for tooling checks; formatting; no public API changes.
Unity helpers (formatting/minor)
UnityMcpBridge/Editor/Helpers/ExecPath.cs, .../McpConfigFileHelper.cs, .../McpLog.cs, .../PackageDetector.cs, .../PackageInstaller.cs, .../PortManager.cs, .../Response.cs, .../ServerInstaller.cs, .../ServerPathResolver.cs, .../Vector3Helper.cs, UnityMcpBridge/Editor/Tools/CommandRegistry.cs, UnityMcpBridge/Editor/Tools/ManageScene.cs, UnityMcpBridge/Editor/Tools/ManageScript.cs, UnityMcpBridge/Editor/Tools/ManageShader.cs, UnityMcpBridge/Editor/External/Tommy.cs, UnityMcpBridge/Runtime/Serialization/UnityTypeConverters.cs, UnityMcpBridge/Editor/AssemblyInfo.cs, UnityMcpBridge/Editor/Helpers/TelemetryHelper.cs
Predominantly whitespace/formatting cleanup; TelemetryHelper signature update noted above. No other behavioral changes.
Unity test project (formatting)
TestProjects/UnityMCPTests/Assets/Scripts/*, .../Assets/Tests/EditMode/Tools/*
Whitespace/comment removals; no logic changes.
Python server/tools (formatting + imports)
UnityMcpBridge/UnityMcpServer~/src/server.py, mcp_source.py, tools/stress_mcp.py
Formatting and import adjustments; telemetry imports added in server.py without changing behavior.
Transport/framing tests (formatting/imports)
test_unity_socket_framing.py, tests/test_transport_framing.py
Import reordering and formatting; behavior unchanged.
Resource tools test fixtures
tests/test_find_in_file_minimal.py, tests/test_resources_api.py, tests/test_read_resource_minimal.py
Introduce/register_resource_tools fixture usage; minor formatting; adds test fixture function.
Telemetry tests scaffolding
tests/test_telemetry_queue_worker.py, tests/test_validate_script_summary.py
Add dummy fastmcp/server mocks for isolation; formatting changes.
Misc tests (formatting)
tests/conftest.py, tests/test_edit_normalization_and_noop.py, tests/test_edit_strict_and_warnings.py, tests/test_get_sha.py, tests/test_improved_anchor_matching.py, tests/test_logging_stdout.py, tests/test_manage_script_uri.py, tests/test_read_console_truncate.py, tests/test_telemetry_endpoint_validation.py, tests/test_telemetry_subaction.py
Formatting/line-wrap only; no logic changes.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant CI as CI Job
  participant MS as mark_skipped.py
  participant FS as Filesystem
  participant XML as JUnit XML

  CI->>MS: Invoke main(path)
  MS->>FS: Read XML file
  MS->>XML: Parse testsuites
  loop Each testsuite
    loop Each testcase
      MS->>XML: Inspect failure/error text
      alt matches skip pattern
        MS->>XML: Remove failure/error
        MS->>XML: Add <skipped reason="pattern">
      end
    end
    MS->>XML: Recompute counts (tests, failures, errors, skipped)
  end
  alt Any change
    MS->>FS: Write updated XML
    MS-->>CI: Exit 0 (updated)
  else No change
    MS-->>CI: Exit 0 (no-op)
  end
Loading
sequenceDiagram
  autonumber
  participant Client as MCP Client
  participant Bridge as MCPForUnityBridge
  participant MG as ManageGameObject
  participant GS as GameObjectSerializer

  Client->>Bridge: Command (e.g., set_component_property)
  Bridge->>Bridge: Validate/deserialize JSON
  Bridge->>MG: Execute request (includes includeNonPublicSerialized)
  alt Prefab target
    MG->>MG: Redirect/deny per action
  else Scene object
    MG->>MG: Resolve object/components/properties
    MG->>GS: Serialize GameObject/Component data
    GS-->>MG: Plain object payload
  end
  MG-->>Bridge: Result or structured error
  Bridge-->>Client: Framed response (status, data/error)
Loading
sequenceDiagram
  autonumber
  participant GH as GitHub Actions
  participant WF as claude-nl-suite.yml
  participant Lic as Licensing Gate
  participant UB as Unity Bridge
  participant NL as NL/T Tests

  GH->>WF: workflow_dispatch / push
  WF->>Lic: Decide ULF vs EBL
  alt ULF available
    Lic-->>WF: Stage ULF
  else EBL
    Lic-->>WF: Activate via Entitlement
  else Fail
    Lic-->>WF: Missing entitlement -> fail job
  end
  WF->>UB: Warm-up and start persistent bridge
  WF->>NL: Run NL pass
  WF->>NL: Run T pass (+ retry/backfill if needed)
  WF->>GH: Publish JUnit + artifacts
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • dsarno
  • justinpbarnett
  • Scriptwonder

Poem

A rabbit taps the keys—tik tik—so neat,
Skips tagged tests, makes bridges greet.
Prefabs hop left; components align,
Telemetry whispers, “all is fine.”
Workflows warren into tidy rows—
Carrots of green checks, a garden grows. 🥕🐇

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Title Check ⚠️ Warning The current title “Autoformat code” suggests only stylistic changes, but the PR also introduces new functions for skipping JUnit tests, updates default parameters in core APIs (GameObjectSerializer and TelemetryHelper), adds a public ManageGameObject method, and restructures multiple workflows alongside code formatting. Please revise the title to capture the main functional changes alongside formatting—for example: “Add JUnit skip logic and default parameters to core APIs, restructure workflows, and apply auto-formatting.”
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
tests/test_manage_script_uri.py (1)

1-41: Fix import order to avoid ModuleNotFoundError.

By importing tools.manage_script on Line 1 you now load the module before inserting the Unity server path into sys.path. In a fresh test environment that path is absent, so the import will fail and the entire test module will abort. Move the import until after the sys.path.insert(...) call (e.g., via importlib.import_module) so the dynamic path setup still works.

-import tools.manage_script as manage_script  # type: ignore
+import importlib
@@
 sys.path.insert(0, str(SRC))
 
+manage_script = importlib.import_module("tools.manage_script")  # type: ignore
UnityMcpBridge/Editor/Windows/MCPForUnityEditorWindow.cs (1)

698-725: Fix invalid cast: JArray isn’t IEnumerable; causes false negatives

Casting dynamic args (JArray) to IEnumerable can throw at runtime and makes IsCursorConfigured always return false on errors. Use non-generic IEnumerable or JArray directly.

Apply this diff:

-                var args = unity.args;
-                if (args == null) return false;
-                // Prefer exact extraction of the --directory value and compare normalized paths
-                string[] strArgs = ((System.Collections.Generic.IEnumerable<object>)args)
-                    .Select(x => x?.ToString() ?? string.Empty)
-                    .ToArray();
+                var argsToken = unity.args;
+                if (argsToken == null) return false;
+                // Prefer exact extraction of the --directory value and compare normalized paths
+                // Handle both JArray and generic IEnumerable safely
+                string[] strArgs;
+                if (argsToken is JArray jarr)
+                {
+                    strArgs = jarr.Select(t => t?.ToString() ?? string.Empty).ToArray();
+                }
+                else if (argsToken is System.Collections.IEnumerable en)
+                {
+                    strArgs = en.Cast<object>().Select(x => x?.ToString() ?? string.Empty).ToArray();
+                }
+                else
+                {
+                    return false;
+                }

(Requires using Newtonsoft.Json.Linq; which this file already has.)

🧹 Nitpick comments (13)
UnityMcpBridge/Editor/Helpers/TelemetryHelper.cs (1)

212-222: Consider consolidating the duplicated IsDebugEnabled pattern.

The IsDebugEnabled() helper appears in multiple files with identical implementations (PortManager.cs, MCPForUnityBridge.cs, McpLog.cs). Consider extracting this to a shared utility class to reduce duplication and improve maintainability.

For example, create a shared helper:

// In a new DebugHelper.cs or add to an existing utility class
public static class DebugPrefs
{
    public static bool IsDebugEnabled()
    {
        try { return UnityEditor.EditorPrefs.GetBool("MCPForUnity.DebugLogs", false); }
        catch { return false; }
    }
}

Then replace all instances with calls to this shared method.

UnityMcpBridge/Editor/Tools/ManageGameObject.cs (5)

2040-2059: Remove unreachable commented code.

The fallback logic at lines 2056-2058 is unreachable because all paths either return or throw. Since the code is commented and dead, consider removing it to reduce clutter.

Apply this diff to remove the unreachable commented code:

             throw;
         }
-        // If ToObject succeeded, it would have returned. If it threw, we wouldn't reach here.
-        // This fallback logic is likely unreachable if ToObject covers all cases or throws on failure.
-        // Debug.LogWarning($"Conversion failed for token to {targetType.FullName}. Token: {token.ToString(Formatting.None)}");
-        // return targetType.IsValueType ? Activator.CreateInstance(targetType) : null;
     }

950-1021: Reverse iteration pattern prevents collection modification issues.

The defensive approach of copying components to a List, nulling the original array reference, and iterating backward (lines 963-1003) avoids potential collection modification exceptions that can occur when reflection triggers property getters that modify the component array. The Insert(0, data) maintains original order in the final result.

This pattern is robust but adds O(n) overhead for the list copy and O(n²) for insertions. Consider using a deque or reversing once at the end if performance becomes a concern with large component counts.

Alternative approach to avoid O(n²) insertions:

// After the reverse loop, reverse the list once
componentData.Reverse();

This would allow appending (componentData.Add(data)) during the loop, then reversing once at the end for O(n) total complexity.


1886-1920: Material shader property handling is comprehensive but could be simplified.

The cascading array-size checks (4, 3, 2 elements) with multiple try-catch blocks for each type conversion are thorough but verbose. The logic correctly handles Color (RGB/RGBA), Vector2/3/4, and Texture assignments.

Consider extracting this into a helper method to reduce duplication and improve readability, especially since similar patterns exist elsewhere.

private static bool TrySetMaterialProperty(Material material, string propName, JToken value, JsonSerializer serializer)
{
    if (value is JArray jArray)
    {
        return TrySetMaterialVector(material, propName, jArray, serializer);
    }
    else if (value.Type == JTokenType.Float || value.Type == JTokenType.Integer)
    {
        try { material.SetFloat(propName, value.ToObject<float>(serializer)); return true; } catch { }
    }
    // ... etc
    return false;
}

2156-2258: New FindObjectByInstruction method enables UnityEngineObjectConverter integration.

This public method provides a centralized lookup mechanism for Unity objects based on JSON find instructions, supporting:

  • Asset database lookups by path/GUID/name
  • Scene object searches by ID/name/path
  • Component resolution on found GameObjects

The method correctly prioritizes asset lookups for asset types (Materials, Textures, etc.) before falling back to scene searches. Ambiguous asset matches appropriately return null with a warning.

Consider adding a cache for repeated lookups of the same instruction to avoid redundant searches:

private static readonly Dictionary<string, WeakReference<UnityEngine.Object>> _findCache = new();

This would benefit workflows that repeatedly reference the same objects across multiple commands.


882-882: Remove trailing empty lines for consistency.

Multiple trailing empty lines throughout the file should be removed to match the project's formatting style.

Apply this pattern at lines 882, 1184, 1233, 2271, 2277, 2281, 2460, and 2485:

-        }
-
+        }

Also applies to: 1184-1184, 1233-1233, 2271-2271, 2277-2277, 2281-2281, 2460-2460, 2485-2485

UnityMcpBridge/Editor/Windows/MCPForUnityEditorWindow.cs (6)

862-888: Avoid repeated ResolveClaude()/FindUvPath() in OnGUI path

OnGUI can run many times per second. Re-resolving CLI paths multiple times per draw adds I/O overhead. Resolve once and reuse locals; also reuse uvPathEarly later (hideConfigInfo).

Apply these minimal edits:

@@
-            // Special pre-check for Claude Code: if CLI missing, reflect in status UI
+            // Special pre-check for Claude Code: cache CLI/UV resolution for this draw
             if (mcpClient.mcpType == McpTypes.ClaudeCode)
             {
-                string claudeCheck = ExecPath.ResolveClaude();
+                string claudePath = ExecPath.ResolveClaude();
+                string claudeCheck = claudePath;
                 if (string.IsNullOrEmpty(claudeCheck))
                 {
                     mcpClient.configStatus = "Claude Not Found";
                     mcpClient.status = McpStatus.NotConfigured;
                 }
             }
@@
-            bool uvRequired = mcpClient.mcpType != McpTypes.ClaudeCode;
-            bool uvMissingEarly = false;
-            if (uvRequired)
-            {
-                string uvPathEarly = FindUvPath();
-                if (string.IsNullOrEmpty(uvPathEarly))
-                {
-                    uvMissingEarly = true;
-                    mcpClient.configStatus = "uv Not Found";
-                    mcpClient.status = McpStatus.NotConfigured;
-                }
-            }
+            bool uvRequired = mcpClient.mcpType != McpTypes.ClaudeCode;
+            string uvPathEarly = uvRequired ? FindUvPath() : null;
+            bool uvMissingEarly = uvRequired && string.IsNullOrEmpty(uvPathEarly);
+            if (uvMissingEarly)
+            {
+                mcpClient.configStatus = "uv Not Found";
+                mcpClient.status = McpStatus.NotConfigured;
+            }
@@
-            if (mcpClient.mcpType == McpTypes.ClaudeCode && string.IsNullOrEmpty(ExecPath.ResolveClaude()))
+            if (mcpClient.mcpType == McpTypes.ClaudeCode && string.IsNullOrEmpty(claudeCheck))
@@
-                bool claudeAvailable = !string.IsNullOrEmpty(ExecPath.ResolveClaude());
+                bool claudeAvailable = !string.IsNullOrEmpty(claudeCheck);
@@
-                    string resolvedClaude = ExecPath.ResolveClaude();
+                    string resolvedClaude = claudeCheck;
@@
-            bool hideConfigInfo =
-                (mcpClient.mcpType == McpTypes.ClaudeCode && string.IsNullOrEmpty(ExecPath.ResolveClaude()))
-                || ((mcpClient.mcpType != McpTypes.ClaudeCode) && string.IsNullOrEmpty(FindUvPath()));
+            bool hideConfigInfo =
+                (mcpClient.mcpType == McpTypes.ClaudeCode && string.IsNullOrEmpty(claudeCheck))
+                || ((mcpClient.mcpType != McpTypes.ClaudeCode) && string.IsNullOrEmpty(uvPathEarly));

This keeps behavior identical while cutting redundant probes.

Also applies to: 903-919, 922-959, 971-1021, 1072-1084


1759-1766: Use validated uv path consistently (avoid ExecPath.ResolveUv fallback to "uv")

Registration should use the same uv discovery/validation as configuration. Falling back to a bare "uv" can fail or pick the wrong binary on systems with multiple versions.

Apply this diff:

-            string uvPath = ExecPath.ResolveUv() ?? "uv";
+            string uvPath = FindUvPath();
+            if (string.IsNullOrEmpty(uvPath))
+            {
+                UnityEngine.Debug.LogError("MCP for Unity: UV package manager not found. Install uv and retry.");
+                return;
+            }

This aligns with ServerInstaller.FindUvPath() semantics used elsewhere.


1815-1842: DRY the Claude server-name list (used in get/remove loops)

The candidate name arrays are duplicated, which risks drift. Centralize once and reuse for both “get” and “remove” passes.

Example:

-            string[] candidateNamesForGet = { "UnityMCP", "unityMCP", "unity-mcp", "UnityMcpServer" };
+            string[] candidateNamesForGet = PossibleClaudeServerNames();
@@
-            string[] possibleNames = { "UnityMCP", "unityMCP", "unity-mcp", "UnityMcpServer" };
+            string[] possibleNames = PossibleClaudeServerNames();

Add a private helper:

private static string[] PossibleClaudeServerNames() => new[] { "UnityMCP", "unityMCP", "unity-mcp", "UnityMcpServer" };

1418-1421: Avoid magic “Configured successfully” sentinel across code paths

Multiple methods compare exact return text to set status. Promote to a private const or switch to a Result enum to prevent fragile string coupling.

For a light touch:

+        private const string ConfigOk = "Configured successfully";
@@
-                if (result == "Configured successfully")
+                if (result == ConfigOk)
                 {
                     mcpClient.SetStatus(McpStatus.Configured);
                 }
@@
-            if (!changed)
-            {
-                return "Configured successfully";
-            }
+            if (!changed) return ConfigOk;
@@
-            return "Configured successfully";
+            return ConfigOk;

Repeat for ConfigureCodexClient.

Also applies to: 1511-1513, 1253-1254


173-221: Layout constants: consider EditorPrefs-backed overrides for widths/heights

Fixed panel sizes can clip on low-DPI or unusually long localized strings. Expose optional EditorPrefs (or a hidden “Advanced Layout” foldout) to tweak colWidth/topPanelHeight/bottomPanelHeight.

No behavior change; improves accessibility across setups.


1901-1979: Claude config parsing: add tolerant checks for array/object types

Dynamic parsing can vary with user edits. Before accessing claudeConfig.projects, guard that it’s an object; normalize case-insensitive lookups for mcpServers. Current code is mostly robust, but a couple extra type checks reduce warning noise.

Add:

if (claudeConfig?.projects is JObject projectsObj) {
   foreach (var prop in projectsObj.Properties()) {
      // use prop.Name/prop.Value safely
   }
}

Keeps log clean on malformed files. Based on learnings.

UnityMcpBridge/Editor/Helpers/GameObjectSerializer.cs (1)

528-528: Remove trailing empty line.

Minor formatting inconsistency: the closing brace should not be followed by an empty line.

     }
-}
-
+}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 47ec46c and c8230d1.

📒 Files selected for processing (58)
  • .github/scripts/mark_skipped.py (5 hunks)
  • .github/workflows/bump-version.yml (1 hunks)
  • .github/workflows/claude-nl-suite.yml (1 hunks)
  • .github/workflows/github-repo-stats.yml (0 hunks)
  • .github/workflows/unity-tests.yml (1 hunks)
  • TestProjects/UnityMCPTests/Assets/Scripts/Hello.cs (0 hunks)
  • TestProjects/UnityMCPTests/Assets/Scripts/LongUnityScriptClaudeTest.cs (0 hunks)
  • TestProjects/UnityMCPTests/Assets/Scripts/TestAsmdef/CustomComponent.cs (2 hunks)
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/AIPropertyMatchingTests.cs (8 hunks)
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ComponentResolverTests.cs (10 hunks)
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageGameObjectTests.cs (18 hunks)
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageScriptValidationTests.cs (11 hunks)
  • UnityMcpBridge/Editor/AssemblyInfo.cs (1 hunks)
  • UnityMcpBridge/Editor/Data/DefaultServerConfig.cs (0 hunks)
  • UnityMcpBridge/Editor/External/Tommy.cs (45 hunks)
  • UnityMcpBridge/Editor/Helpers/ExecPath.cs (1 hunks)
  • UnityMcpBridge/Editor/Helpers/GameObjectSerializer.cs (13 hunks)
  • UnityMcpBridge/Editor/Helpers/McpConfigFileHelper.cs (0 hunks)
  • UnityMcpBridge/Editor/Helpers/McpLog.cs (0 hunks)
  • UnityMcpBridge/Editor/Helpers/PackageDetector.cs (0 hunks)
  • UnityMcpBridge/Editor/Helpers/PackageInstaller.cs (2 hunks)
  • UnityMcpBridge/Editor/Helpers/PortManager.cs (4 hunks)
  • UnityMcpBridge/Editor/Helpers/Response.cs (0 hunks)
  • UnityMcpBridge/Editor/Helpers/ServerInstaller.cs (3 hunks)
  • UnityMcpBridge/Editor/Helpers/ServerPathResolver.cs (0 hunks)
  • UnityMcpBridge/Editor/Helpers/TelemetryHelper.cs (11 hunks)
  • UnityMcpBridge/Editor/Helpers/Vector3Helper.cs (0 hunks)
  • UnityMcpBridge/Editor/MCPForUnityBridge.cs (5 hunks)
  • UnityMcpBridge/Editor/Tools/CommandRegistry.cs (0 hunks)
  • UnityMcpBridge/Editor/Tools/ManageGameObject.cs (20 hunks)
  • UnityMcpBridge/Editor/Tools/ManageScene.cs (0 hunks)
  • UnityMcpBridge/Editor/Tools/ManageScript.cs (0 hunks)
  • UnityMcpBridge/Editor/Tools/ManageShader.cs (1 hunks)
  • UnityMcpBridge/Editor/Tools/ReadConsole.cs (3 hunks)
  • UnityMcpBridge/Editor/Windows/MCPForUnityEditorWindow.cs (36 hunks)
  • UnityMcpBridge/Editor/Windows/VSCodeManualSetupWindow.cs (4 hunks)
  • UnityMcpBridge/Runtime/Serialization/UnityTypeConverters.cs (3 hunks)
  • UnityMcpBridge/UnityMcpServer~/src/server.py (9 hunks)
  • mcp_source.py (4 hunks)
  • test_unity_socket_framing.py (5 hunks)
  • tests/conftest.py (0 hunks)
  • tests/test_edit_normalization_and_noop.py (5 hunks)
  • tests/test_edit_strict_and_warnings.py (4 hunks)
  • tests/test_find_in_file_minimal.py (3 hunks)
  • tests/test_get_sha.py (2 hunks)
  • tests/test_improved_anchor_matching.py (5 hunks)
  • tests/test_logging_stdout.py (1 hunks)
  • tests/test_manage_script_uri.py (4 hunks)
  • tests/test_read_console_truncate.py (5 hunks)
  • tests/test_read_resource_minimal.py (3 hunks)
  • tests/test_resources_api.py (4 hunks)
  • tests/test_script_tools.py (8 hunks)
  • tests/test_telemetry_endpoint_validation.py (1 hunks)
  • tests/test_telemetry_queue_worker.py (2 hunks)
  • tests/test_telemetry_subaction.py (1 hunks)
  • tests/test_transport_framing.py (3 hunks)
  • tests/test_validate_script_summary.py (3 hunks)
  • tools/stress_mcp.py (10 hunks)
💤 Files with no reviewable changes (14)
  • UnityMcpBridge/Editor/Helpers/Response.cs
  • UnityMcpBridge/Editor/Helpers/McpConfigFileHelper.cs
  • TestProjects/UnityMCPTests/Assets/Scripts/Hello.cs
  • .github/workflows/github-repo-stats.yml
  • UnityMcpBridge/Editor/Helpers/PackageDetector.cs
  • UnityMcpBridge/Editor/Helpers/Vector3Helper.cs
  • UnityMcpBridge/Editor/Helpers/McpLog.cs
  • TestProjects/UnityMCPTests/Assets/Scripts/LongUnityScriptClaudeTest.cs
  • UnityMcpBridge/Editor/Tools/ManageScript.cs
  • UnityMcpBridge/Editor/Tools/ManageScene.cs
  • tests/conftest.py
  • UnityMcpBridge/Editor/Tools/CommandRegistry.cs
  • UnityMcpBridge/Editor/Helpers/ServerPathResolver.cs
  • UnityMcpBridge/Editor/Data/DefaultServerConfig.cs
🧰 Additional context used
🧬 Code graph analysis (22)
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ComponentResolverTests.cs (2)
UnityMcpBridge/Editor/Tools/ManageGameObject.cs (3)
  • ComponentResolver (2286-2546)
  • TryResolve (2296-2332)
  • Type (2265-2279)
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/AIPropertyMatchingTests.cs (14)
  • Test (32-41)
  • Test (43-49)
  • Test (51-57)
  • Test (59-65)
  • Test (67-73)
  • Test (75-82)
  • Test (84-92)
  • Test (94-100)
  • Test (102-109)
  • Test (111-118)
  • Test (120-133)
  • Test (135-147)
  • Test (149-158)
  • Test (160-168)
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageScriptValidationTests.cs (1)
UnityMcpBridge/Editor/Tools/ManageScript.cs (1)
  • ManageScript (52-2552)
tests/test_edit_normalization_and_noop.py (2)
tests/test_edit_strict_and_warnings.py (5)
  • _load (30-34)
  • DummyMCP (40-45)
  • tool (43-45)
  • deco (44-44)
  • setup_tools (48-51)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_script_edits.py (1)
  • register_manage_script_edits_tools (309-968)
tests/test_read_resource_minimal.py (3)
tests/test_find_in_file_minimal.py (1)
  • resource_tools (26-29)
tests/test_resources_api.py (1)
  • resource_tools (38-41)
UnityMcpBridge/UnityMcpServer~/src/tools/resource_tools.py (2)
  • register_resource_tools (136-396)
  • read_resource (194-350)
UnityMcpBridge/Editor/Helpers/PackageInstaller.cs (1)
UnityMcpBridge/Editor/Helpers/ServerInstaller.cs (2)
  • ServerInstaller (12-743)
  • EnsureServerInstalled (22-110)
UnityMcpBridge/Editor/Helpers/TelemetryHelper.cs (3)
UnityMcpBridge/Editor/Helpers/PortManager.cs (1)
  • IsDebugEnabled (19-23)
UnityMcpBridge/Editor/MCPForUnityBridge.cs (1)
  • IsDebugEnabled (63-66)
UnityMcpBridge/Editor/Helpers/McpLog.cs (1)
  • IsDebugEnabled (10-13)
tests/test_resources_api.py (2)
tests/test_find_in_file_minimal.py (3)
  • resource_tools (26-29)
  • tool (18-22)
  • deco (19-21)
tests/test_read_resource_minimal.py (3)
  • resource_tools (43-46)
  • tool (35-39)
  • deco (36-38)
tests/test_script_tools.py (5)
tests/test_edit_normalization_and_noop.py (2)
  • _Dummy (17-18)
  • DummyMCP (42-47)
tests/test_improved_anchor_matching.py (2)
  • _Dummy (21-22)
  • load_module (34-38)
tests/test_manage_script_uri.py (2)
  • _Dummy (30-31)
  • DummyMCP (46-54)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_script.py (1)
  • register_manage_script_tools (22-565)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_asset.py (1)
  • register_manage_asset_tools (13-86)
tests/test_read_console_truncate.py (2)
tests/test_get_sha.py (2)
  • _load_module (30-34)
  • DummyMCP (41-49)
UnityMcpBridge/UnityMcpServer~/src/tools/read_console.py (2)
  • register_read_console_tools (12-90)
  • read_console (17-90)
tests/test_improved_anchor_matching.py (2)
tests/test_script_tools.py (1)
  • load_module (32-36)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_script_edits.py (2)
  • _find_best_anchor_match (94-133)
  • _apply_edits_locally (12-91)
tests/test_telemetry_endpoint_validation.py (1)
UnityMcpBridge/UnityMcpServer~/src/telemetry.py (1)
  • TelemetryCollector (190-376)
tests/test_validate_script_summary.py (2)
tests/test_get_sha.py (3)
  • _Dummy (17-18)
  • _load_module (30-34)
  • DummyMCP (41-49)
tests/test_manage_script_uri.py (2)
  • _Dummy (30-31)
  • DummyMCP (46-54)
tests/test_manage_script_uri.py (1)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_script.py (1)
  • manage_script (456-513)
tests/test_transport_framing.py (1)
UnityMcpBridge/UnityMcpServer~/src/unity_connection.py (1)
  • UnityConnection (32-371)
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageGameObjectTests.cs (3)
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/AIPropertyMatchingTests.cs (15)
  • SetUp (14-30)
  • Test (32-41)
  • Test (43-49)
  • Test (51-57)
  • Test (59-65)
  • Test (67-73)
  • Test (75-82)
  • Test (84-92)
  • Test (94-100)
  • Test (102-109)
  • Test (111-118)
  • Test (120-133)
  • Test (135-147)
  • Test (149-158)
  • Test (160-168)
UnityMcpBridge/Editor/Tools/ManageGameObject.cs (9)
  • GameObject (1268-1291)
  • ManageGameObject (22-2280)
  • HandleCommand (41-186)
  • ComponentResolver (2286-2546)
  • List (1296-1455)
  • List (2347-2384)
  • List (2407-2425)
  • List (2430-2469)
  • List (2477-2512)
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ComponentResolverTests.cs (2)
  • Test (11-19)
  • Test (21-29)
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/AIPropertyMatchingTests.cs (1)
UnityMcpBridge/Editor/Tools/ManageGameObject.cs (6)
  • ComponentResolver (2286-2546)
  • List (1296-1455)
  • List (2347-2384)
  • List (2407-2425)
  • List (2430-2469)
  • List (2477-2512)
tests/test_get_sha.py (1)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_script.py (1)
  • manage_script (456-513)
UnityMcpBridge/Editor/Windows/MCPForUnityEditorWindow.cs (7)
UnityMcpBridge/Editor/Helpers/CodexConfigHelper.cs (4)
  • CodexConfigHelper (17-242)
  • IsCodexConfigured (19-41)
  • BuildCodexServerBlock (43-49)
  • TryParseCodexServer (105-145)
UnityMcpBridge/Editor/Helpers/ExecPath.cs (2)
  • ExecPath (11-277)
  • ResolveClaude (16-89)
UnityMcpBridge/Editor/Helpers/ServerInstaller.cs (2)
  • FindUvPath (529-717)
  • ServerInstaller (12-743)
UnityMcpBridge/Editor/Helpers/McpConfigFileHelper.cs (2)
  • McpConfigFileHelper (14-185)
  • WriteAtomicFile (100-150)
UnityMcpBridge/Editor/Helpers/ConfigJsonBuilder.cs (2)
  • ConfigJsonBuilder (7-128)
  • BuildManualConfigJson (9-29)
UnityMcpBridge/Editor/Windows/ManualConfigEditorWindow.cs (2)
  • ManualConfigEditorWindow (9-295)
  • ShowWindow (19-27)
UnityMcpBridge/Editor/Helpers/ServerPathResolver.cs (2)
  • ServerPathResolver (8-148)
  • TryFindEmbeddedServerSource (15-100)
UnityMcpBridge/UnityMcpServer~/src/server.py (2)
UnityMcpBridge/UnityMcpServer~/src/telemetry.py (5)
  • record_telemetry (391-395)
  • record_milestone (249-271)
  • record_milestone (398-400)
  • RecordType (37-46)
  • MilestoneType (49-57)
UnityMcpBridge/UnityMcpServer~/src/unity_connection.py (1)
  • get_unity_connection (378-399)
UnityMcpBridge/Editor/MCPForUnityBridge.cs (3)
UnityMcpBridge/Editor/Helpers/TelemetryHelper.cs (3)
  • TelemetryHelper (12-223)
  • RecordBridgeStartup (139-146)
  • IsDebugEnabled (212-222)
UnityMcpBridge/Editor/Helpers/McpLog.cs (5)
  • IsDebugEnabled (10-13)
  • McpLog (6-30)
  • Info (15-19)
  • Warn (21-24)
  • Error (26-29)
UnityMcpBridge/Editor/Models/Command.cs (1)
  • Command (8-19)
tests/test_find_in_file_minimal.py (2)
tests/test_read_resource_minimal.py (2)
  • resource_tools (43-46)
  • DummyMCP (31-39)
UnityMcpBridge/UnityMcpServer~/src/tools/resource_tools.py (2)
  • register_resource_tools (136-396)
  • find_in_file (354-396)
UnityMcpBridge/Editor/Tools/ManageGameObject.cs (3)
UnityMcpBridge/Runtime/Serialization/UnityTypeConverters.cs (13)
  • Vector2Converter (36-56)
  • QuaternionConverter (58-84)
  • ColorConverter (86-112)
  • RectConverter (114-140)
  • BoundsConverter (142-161)
  • UnityEngineObjectConverter (164-265)
  • Color (102-111)
  • Vector3 (25-33)
  • Vector2 (48-55)
  • Quaternion (74-83)
  • Rect (130-139)
  • Bounds (154-160)
  • UnityEngine (222-264)
UnityMcpBridge/Editor/Tools/ManageAsset.cs (1)
  • ConvertJTokenToType (1158-1235)
UnityMcpBridge/Editor/Helpers/GameObjectSerializer.cs (1)
  • JToken (507-526)
🪛 Ruff (0.13.1)
tests/test_edit_normalization_and_noop.py

45-45: Unused method argument: args

(ARG002)


45-45: Unused method argument: kwargs

(ARG002)


46-46: Multiple statements on one line (semicolon)

(E702)


83-83: Unused function argument: cmd

(ARG001)


98-98: Unused function argument: cmd

(ARG001)


98-98: Unused function argument: params

(ARG001)

mcp_source.py

81-82: Avoid specifying long messages outside the exception class

(TRY003)

tests/test_resources_api.py

30-30: Unused method argument: args

(ARG002)


30-30: Unused method argument: kwargs

(ARG002)

tests/test_improved_anchor_matching.py

161-161: Do not assert False (python -O removes these calls), raise AssertionError()

Replace assert False

(B011)

tools/stress_mcp.py

308-309: try-except-pass detected, consider logging the exception

(S110)


308-308: Do not catch blind exception: Exception

(BLE001)

tests/test_transport_framing.py

188-189: try-except-pass detected, consider logging the exception

(S110)


188-188: Do not catch blind exception: Exception

(BLE001)

test_unity_socket_framing.py

32-32: Consider moving this statement to an else block

(TRY300)


33-33: Do not catch blind exception: Exception

(BLE001)


93-94: Avoid specifying long messages outside the exception class

(TRY003)

🔇 Additional comments (63)
TestProjects/UnityMCPTests/Assets/Scripts/TestAsmdef/CustomComponent.cs (1)

9-9: LGTM! Standard formatting improvements.

The blank line between serialized fields and the trailing newline at end of file align with common C# formatting conventions.

Also applies to: 18-18

tests/test_edit_normalization_and_noop.py (1)

15-16: LGTM! Formatting changes align with autoformat objective.

The changes are purely cosmetic—adding blank lines and splitting long function calls for readability. No logic or behavior is altered.

Also applies to: 17-18, 29-29, 36-36, 38-39, 44-44, 49-49, 72-73, 82-82, 88-89, 91-91, 97-97, 102-103, 111-112, 115-115, 125-126, 128-129

UnityMcpBridge/Editor/Windows/VSCodeManualSetupWindow.cs (5)

16-16: LGTM! Formatting improvement.

The blank line improves readability by visually separating the window configuration from the subsequent comment and object initialization.


23-23: LGTM! Formatting improvement.

The blank line appropriately separates the mcpClient initialization from the window.Show() call.


87-87: LGTM! Formatting improvement.

The blank line appropriately separates the prerequisites section from the configuration steps section.


105-105: LGTM! Formatting improvement.

The blank line appropriately separates the configuration steps from the path location section.


123-123: LGTM! Formatting cleanup.

The else keyword formatting has been cleaned up, removing any extraneous whitespace.

UnityMcpBridge/Editor/Helpers/PortManager.cs (4)

46-47: LGTM! Improved readability of complex condition.

The multi-line formatting makes the compound boolean expression easier to read and maintain.


231-231: LGTM! Better visual separation.

The blank line improves readability by separating variable initialization from the conditional logic.


264-264: LGTM! Consistent formatting improvement.

The blank line provides consistent visual separation, matching the formatting in LoadStoredPort method.


319-319: LGTM! Standard EOF formatting.

The end-of-file newline follows standard formatting conventions.

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

15-359: LGTM! Formatting improvements enhance readability.

The changes are limited to blank line additions that improve visual separation between logical sections of test methods. No functional changes to test logic or assertions.

tests/test_telemetry_endpoint_validation.py (1)

4-63: LGTM! Formatting aligns with Python conventions.

The changes wrap long importlib.import_module calls across multiple lines for better readability and add blank lines to improve visual structure. No functional changes to test logic.

UnityMcpBridge/Editor/Helpers/ExecPath.cs (1)

208-208: LGTM! Spacing normalized to standard C# conventions.

The change normalizes the spacing around the += operator from two spaces to one, aligning with typical C# formatting standards. No functional change to the event handler subscription.

UnityMcpBridge/Editor/Tools/ManageShader.cs (1)

342-342: LGTM!

End-of-file newline added per standard formatting conventions.

tests/test_telemetry_subaction.py (1)

6-7: LGTM!

Multiline formatting improves readability with no behavioral change.

UnityMcpBridge/Editor/AssemblyInfo.cs (1)

3-3: LGTM!

Formatting adjustment with no semantic change to the InternalsVisibleTo declaration.

UnityMcpBridge/Runtime/Serialization/UnityTypeConverters.cs (1)

113-113: LGTM!

Whitespace formatting adjustments with no functional changes.

Also applies to: 141-141, 266-266

tests/test_logging_stdout.py (1)

67-70: LGTM!

Line continuation improves readability of long assertion messages without changing test logic.

UnityMcpBridge/Editor/Helpers/TelemetryHelper.cs (1)

169-184: LGTM! Optional error parameter enhances telemetry.

The addition of the optional error parameter to RecordToolExecution allows capturing failure details without breaking existing call sites. The conditional assignment of the error field is handled correctly.

UnityMcpBridge/UnityMcpServer~/src/server.py (2)

1-1: LGTM! Telemetry imports align with broader integration.

The addition of telemetry imports (record_telemetry, record_milestone, RecordType, MilestoneType) is consistent with the telemetry enhancements across the codebase, including the TelemetryHelper.cs changes.


25-50: Formatting adjustments improve readability.

The multi-line formatting of log path construction, handler instantiation, and logger configuration improves code readability without changing behavior.

tests/test_validate_script_summary.py (1)

1-77: LGTM! Formatting adjustments maintain test functionality.

The formatting changes (blank lines, multi-line call wrapping) improve readability without altering test behavior. The test scaffolding setup remains intact and correctly configured.

UnityMcpBridge/Editor/External/Tommy.cs (1)

1-2139: LGTM! Formatting-only changes to external TOML parser.

All changes in this file are formatting adjustments (whitespace, line breaks, expression alignment) with no semantic or API modifications. The MIT-licensed Tommy TOML parser functionality remains unchanged.

.github/scripts/mark_skipped.py (3)

33-40: LGTM! Public function extraction improves reusability.

Extracting should_skip() as a public function enables reuse of the skip-pattern logic in other contexts. The pattern matching against environmental/permission failures is appropriate.


43-56: LGTM! Testsuite summary logic is correct.

The summarize_counts() function correctly tallies tests, failures, errors, and skipped cases for JUnit XML testsuite elements.


59-110: LGTM! Main orchestration logic handles XML updates correctly.

The main() function properly:

  • Parses JUnit XML and handles parse errors gracefully
  • Identifies failing tests matching skip patterns
  • Converts failures to skipped entries with appropriate messages
  • Recomputes suite tallies after processing
  • Only writes changes when modifications occurred

The multi-line formatting for first_match_text assignment (lines 85-86) and print statement (lines 105-106) improves readability.

UnityMcpBridge/Editor/Helpers/GameObjectSerializer.cs (7)

122-122: LGTM: Backwards-compatible API enhancement.

The new optional parameter includeNonPublicSerializedFields with a default value of true preserves existing behavior while adding flexibility. The parameter is correctly threaded through the caching mechanism (line 230) and field filtering logic (lines 266-275).


247-250: LGTM: Correctly handles property overrides.

The duplicate check using Any() ensures only the most derived version of an overridden property is cached, which is the correct behavior for reflection-based serialization.


314-353: LGTM: Robust filtering for problematic Unity properties.

The consolidated skip logic using a skipProperty flag is clean and maintainable. It correctly filters out:

  • Obsolete component shortcuts (rigidbody, camera, etc.)
  • Matrix properties prone to cycles
  • Camera-specific problematic properties

This defensive approach prevents serialization crashes reported in Unity's API.


462-474: LGTM: Comprehensive JToken type handling.

The additional cases for Date, Guid, Uri, TimeSpan, Bytes, and Undefined provide robust coverage of Newtonsoft.Json token types. Treating Undefined as null is a sensible default.


122-122: LGTM: Backward-compatible signature enhancement.

Adding the default parameter includeNonPublicSerializedFields = true preserves existing behavior while enabling callers to exclude non-public serialized fields when needed. The default aligns with Unity's serialization model.


462-474: Enhanced JToken type coverage improves robustness.

The addition of explicit handling for Date, Guid, Uri, TimeSpan, Bytes, and Undefined token types ensures more comprehensive serialization support. The fallback to jValue.Value for unlisted types provides graceful degradation.


247-250: Verify override handling for hidden and explicit-interface properties

Ensure that properties using C#’s new modifier or explicit interface implementations (same Name but different DeclaringType) are correctly skipped or retained by the propertiesToCache.Any(p => p.Name == propInfo.Name) check. Add targeted test cases—e.g., a component with a hidden property or an explicit-interface property—to confirm only the intended (most-derived or interface) property is cached.

UnityMcpBridge/Editor/MCPForUnityBridge.cs (12)

520-552: LGTM: Improved handshake error handling.

The restructured handshake logic with explicit try-catch blocks and early returns on failure prevents cascading errors and improves debuggability. The guarded logging prevents exceptions in the error path from causing additional problems.


573-583: LGTM: Efficient ping handling optimization.

Handling the ping command before JSON deserialization is a smart optimization for health checks. This reduces latency and CPU overhead for frequent heartbeat/health check operations.


604-621: LGTM: Robust timeout and error response handling.

The Task.WhenAny pattern correctly implements timeout detection without blocking indefinitely. Structured error responses with explicit status and error fields enable clients to distinguish timeouts from other failures and recover gracefully.


627-652: LGTM: Comprehensive serialization error handling.

The guarded serialization path with try-catch blocks, detailed logging, and timing diagnostics significantly improves debuggability and prevents crashes from malformed responses. The IoInfo logging pattern is consistent and helpful for troubleshooting network issues.


810-826: LGTM: Reduced lock contention via snapshot pattern.

Moving heartbeat processing outside the lock and using a snapshot of the command queue reduces contention on lockObj. This improves responsiveness and prevents the heartbeat from being delayed by long-running command processing.


836-879: LGTM: Comprehensive command validation.

The layered validation (empty check → ping handling → JSON validation → deserialization) prevents crashes from malformed input and provides clear, structured error responses. This defensive approach significantly improves robustness.


93-105: LGTM: Telemetry integration enhances observability.

The addition of TelemetryHelper.RecordBridgeStartup() and RecordBridgeConnection() calls provides valuable instrumentation for monitoring bridge lifecycle events. Error messages are appropriately truncated (200 chars) to prevent excessive logging.


520-552: Improved error handling in client connection setup.

The refactored handshake logic now properly wraps connection setup, Nagle disable, and handshake write in separate try-catch blocks. This provides clearer diagnostics and ensures the client handler exits cleanly on failure. The timeout for handshake write (FrameIOTimeoutMs) prevents indefinite hangs.


605-611: Structured timeout response improves client error handling.

Returning a structured JSON error (status: "error") when command processing times out allows clients to distinguish timeouts from other failures and implement appropriate retry logic. The timeout duration is exposed in the message for debugging.


628-652: Comprehensive serialization error logging aids diagnostics.

The added error logging around response serialization (IoInfo calls) captures sequence numbers, lengths, and durations for both successful writes and failures. This provides valuable forensics for diagnosing protocol-level issues without impacting the happy path.


810-826: Snapshot-based command processing reduces lock contention.

Moving the queue snapshot outside the processing loop (lines 820-825) minimizes the time spent holding lockObj, reducing contention when commands arrive at high frequency. The List copy is cheap and avoids holding the lock during potentially long-running command execution.


836-879: Robust command validation prevents crashes from malformed input.

The multi-layered validation (empty check, ping special-case, JSON validation via IsValidJson, null-command check) ensures that malformed or unexpected input doesn't crash the bridge. Each failure path returns a structured error, maintaining protocol consistency.

UnityMcpBridge/Editor/Tools/ManageGameObject.cs (6)

25-37: LGTM: Efficient shared serializer pattern.

Creating a single InputSerializer instance with all necessary converters avoids repeated allocations and ensures consistent serialization behavior across all command processing operations.


1696-1763: LGTM: Robust property setting with shared serializer.

The refactored SetProperty correctly uses the shared inputSerializer for conversions, provides detailed error messages with formatted token values, and handles properties, public fields, and [SerializeField] private fields. This covers the common Unity patterns comprehensively.


2430-2512: LGTM: Pragmatic property suggestion system.

The GetAIPropertySuggestions method uses a sensible rule-based approach with caching. The combination of exact matching, word containment, and Levenshtein distance provides useful suggestions without requiring external AI services. The threshold (Math.Max(2, cleanedInput.Length / 4)) at line 2502 balances sensitivity and precision well.


950-1021: Clarify reverse iteration in GetComponentsFromTarget
ManageGameObject.cs (lines 972–986) uses a reverse for-loop and Insert(0) to rebuild the list, but no internal comments or references explain why. Unless this addresses a specific Unity serialization or ordering quirk, please confirm its necessity or simplify by iterating forward and using List.Add() instead.


30-38: LGTM: Vector2Converter addition completes serializer setup.

Adding Vector2Converter to InputSerializer ensures consistent handling of Vector2 properties alongside the existing Vector3, Quaternion, and other Unity type converters.


2040-2058: ConvertJTokenToType exceptions are already handled: Both SetProperty (lines 1680–1770) and SetNestedProperty (lines 1770–1870) wrap all calls in a try/catch(Exception), logging errors and returning failure, so no unhandled exceptions will escape.

.github/workflows/claude-nl-suite.yml (8)

1-69: LGTM: Workflow setup and MCP installation.

The secrets detection, Python environment setup, and MCP server installation logic with fallback paths are well-structured and properly handle different project layouts.


161-220: LGTM: Unity warm-up and persistent bridge startup.

The warm-up step ensures the Unity Library is imported before the bridge starts, and the persistent container is properly configured with necessary mounts (workspace, status dir, Unity config/local as read-only). The conditional license handling is correct.


222-275: LGTM: Robust Unity bridge health check.

The wait logic is comprehensive with multiple signal sources (status JSON, log markers, container state) and a grace period to allow licensing to settle. Sensitive data is properly redacted in error logs.


278-366: LGTM: MCP configuration and report setup.

The MCP client configuration, tool permissions, report directory structure, and bridge status verification are all properly implemented. The bootstrap placeholder in the JUnit skeleton is correctly designed to be replaced later.


460-493: Verify consistency of T coverage assertions.

The pre-retry coverage check (lines 426-427) allows fragments in either reports/ or reports/_staging/, and the post-promotion assertion (lines 485-487) also accepts staged fragments. However, the post-retry assertion (lines 467) only checks reports/ and fails if any fragment is missing there, without checking _staging/. This inconsistency could cause false failures if the retry writes to staging instead of directly to reports.

Consider whether the post-retry assertion should also check _staging/ to align with the other coverage checks.


495-636: LGTM: Test name canonicalization and backfill logic.

The canonicalization script properly normalizes test names using filename-derived IDs as the source of truth, preventing misclassification. The backfill step ensures missing tests are represented with failure placeholders for complete reporting.


638-967: LGTM: JUnit assembly, reporting, and cleanup.

The JUnit assembly logic robustly handles fragments (including malformed XML), enforces one testcase per fragment, and repairs names as needed. Markdown generation, artifact uploading, and cleanup (Unity stop, license return) are all properly implemented.


150-157: Drop the undefined‐output concern. Referencing steps.ulf.outputs.ok when the ulf step is skipped simply expands to an empty string, which evaluates as “false” in both the Bash [[ … == "true" ]] checks and the downstream ULF_OK env var logic (§ steps context, missing keys → empty string). (docs.github.com)

Likely an incorrect or invalid review comment.

tests/test_read_resource_minimal.py (1)

1-73: LGTM: Formatting improvements.

The import reorganization (moving register_resource_tools to the top), additional blank lines for readability, and multi-line function call formatting are all consistent with best practices. No logic changes detected.

tests/test_resources_api.py (1)

1-87: LGTM: Test fixture and formatting improvements.

The addition of register_resource_tools import and fixture call aligns with the pattern in other test files, establishing consistent resource tool registration. The multi-line formatting improves readability.

Note: The static analysis warning about unused *args, **kwargs in the tool method (line 30) is a false positive—these parameters are required for the decorator pattern to accept arbitrary arguments from callers.

tests/test_find_in_file_minimal.py (1)

1-50: LGTM: Resource tools fixture and formatting.

The new resource_tools fixture (lines 25-29) establishes consistent test infrastructure matching the pattern in other test files. The import reorganization and multi-line formatting improve code clarity. No logic changes detected.

@@ -1,3 +1,4 @@
from unity_connection import UnityConnection
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 | 🔴 Critical

Restore deferred UnityConnection import to keep dynamic path discovery working

Importing UnityConnection before we modify sys.path defeats the dynamic lookup just below; in clean test environments the module won’t be importable yet, so this now raises ModuleNotFoundError. Move the import back to after the sys.path.insert to keep the tests runnable.

-from unity_connection import UnityConnection
 import sys
 import json
 ...
 sys.path.insert(0, str(SRC))
 
-from unity_connection import UnityConnection
+from unity_connection import UnityConnection
📝 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
from unity_connection import UnityConnection
# tests/test_transport_framing.py
import sys
import json
...
# ensure our src directory is on the import path before loading UnityConnection
sys.path.insert(0, str(SRC))
from unity_connection import UnityConnection
...
🤖 Prompt for AI Agents
In tests/test_transport_framing.py around lines 1 to 1, the file currently
imports UnityConnection before the test modifies sys.path which breaks dynamic
path discovery and causes ModuleNotFoundError in clean test environments; move
the "from unity_connection import UnityConnection" statement down so it occurs
after the sys.path.insert that adds the project/test path, ensuring the import
happens only once the path is adjusted and leaving dynamic module lookup intact.

Comment on lines +574 to +583
if (commandText.Trim() == "ping")
{
// Direct response to ping without going through JSON parsing
byte[] pingResponseBytes = System.Text.Encoding.UTF8.GetBytes(
/*lang=json,strict*/
"{\"status\":\"success\",\"result\":{\"message\":\"pong\"}}"
);
await WriteFrameAsync(stream, pingResponseBytes);
continue;
}
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 | 🔴 Critical

🧩 Analysis chain

Optimize ping fast-path to avoid redundant JSON parsing.

The special-case handling for bare "ping" commands bypasses JSON deserialization entirely, improving latency for health checks. Consider documenting this optimization in the protocol specification, as it creates an expectation that ping must be sent as raw text (not JSON).


🌐 Web query:

Does the unity-mcp protocol specification document the expected format for ping commands?

💡 Result:

Yes. The MCP specification documents ping as a JSON‑RPC 2.0 request with method "ping" (no params) and an expected empty-result response (e.g., request {"jsonrpc":"2.0","id":"...","method":"ping"} and response {"jsonrpc":"2.0","id":"...","result":{}}). [1][2]

Sources:
[1] MCP Ping specification (message format & behavior).
[2] MCP protocol message-format reference (JSON‑RPC ping example).


Ensure ping fast-path conforms to JSON-RPC spec
The MCP protocol spec defines ping as a JSON-RPC 2.0 request with method "ping" and an empty-result response; the current fast-path bypasses JSON parsing for bare "ping" text, deviating from spec. Either adjust the fast-path to accept proper JSON-RPC ping messages or update the protocol specification to reflect this behavior.

Comment on lines +2156 to +2258
/// <summary>
/// Finds a specific UnityEngine.Object based on a find instruction JObject.
/// Primarily used by UnityEngineObjectConverter during deserialization.
/// </summary>
// Made public static so UnityEngineObjectConverter can call it. Moved from ConvertJTokenToType.
public static UnityEngine.Object FindObjectByInstruction(JObject instruction, Type targetType)
{
string findTerm = instruction["find"]?.ToString();
string method = instruction["method"]?.ToString()?.ToLower();
string componentName = instruction["component"]?.ToString(); // Specific component to get

if (string.IsNullOrEmpty(findTerm))
{
Debug.LogWarning("Find instruction missing 'find' term.");
return null;
}

// Use a flexible default search method if none provided
string searchMethodToUse = string.IsNullOrEmpty(method) ? "by_id_or_name_or_path" : method;

// If the target is an asset (Material, Texture, ScriptableObject etc.) try AssetDatabase first
if (typeof(Material).IsAssignableFrom(targetType) ||
typeof(Texture).IsAssignableFrom(targetType) ||
typeof(ScriptableObject).IsAssignableFrom(targetType) ||
targetType.FullName.StartsWith("UnityEngine.U2D") || // Sprites etc.
typeof(AudioClip).IsAssignableFrom(targetType) ||
typeof(AnimationClip).IsAssignableFrom(targetType) ||
typeof(Font).IsAssignableFrom(targetType) ||
typeof(Shader).IsAssignableFrom(targetType) ||
typeof(ComputeShader).IsAssignableFrom(targetType) ||
typeof(GameObject).IsAssignableFrom(targetType) && findTerm.StartsWith("Assets/")) // Prefab check
{
// Try loading directly by path/GUID first
UnityEngine.Object asset = AssetDatabase.LoadAssetAtPath(findTerm, targetType);
if (asset != null) return asset;
asset = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(findTerm); // Try generic if type specific failed
if (asset != null && targetType.IsAssignableFrom(asset.GetType())) return asset;


// If direct path failed, try finding by name/type using FindAssets
string searchFilter = $"t:{targetType.Name} {System.IO.Path.GetFileNameWithoutExtension(findTerm)}"; // Search by type and name
string[] guids = AssetDatabase.FindAssets(searchFilter);

if (guids.Length == 1)
{
asset = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(guids[0]), targetType);
if (asset != null) return asset;
}
else if (guids.Length > 1)
{
Debug.LogWarning($"[FindObjectByInstruction] Ambiguous asset find: Found {guids.Length} assets matching filter '{searchFilter}'. Provide a full path or unique name.");
// Optionally return the first one? Or null? Returning null is safer.
return null;
}
// If still not found, fall through to scene search (though unlikely for assets)
}


// --- Scene Object Search ---
// Find the GameObject using the internal finder
GameObject foundGo = FindObjectInternal(new JValue(findTerm), searchMethodToUse);

if (foundGo == null)
{
// Don't warn yet, could still be an asset not found above
// Debug.LogWarning($"Could not find GameObject using instruction: {instruction}");
return null;
}

// Now, get the target object/component from the found GameObject
if (targetType == typeof(GameObject))
{
return foundGo; // We were looking for a GameObject
}
else if (typeof(Component).IsAssignableFrom(targetType))
{
Type componentToGetType = targetType;
if (!string.IsNullOrEmpty(componentName))
{
Type specificCompType = FindType(componentName);
if (specificCompType != null && typeof(Component).IsAssignableFrom(specificCompType))
{
componentToGetType = specificCompType;
}
else
{
Debug.LogWarning($"Could not find component type '{componentName}' specified in find instruction. Falling back to target type '{targetType.Name}'.");
}
}

Component foundComp = foundGo.GetComponent(componentToGetType);
if (foundComp == null)
{
Debug.LogWarning($"Found GameObject '{foundGo.name}' but could not find component of type '{componentToGetType.Name}'.");
}
return foundComp;
}
else
{
Debug.LogWarning($"Find instruction handling not implemented for target type: {targetType.Name}");
return null;
}
}
if (asset != null) return asset;
asset = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(findTerm); // Try generic if type specific failed
if (asset != null && targetType.IsAssignableFrom(asset.GetType())) return asset;


// If direct path failed, try finding by name/type using FindAssets
string searchFilter = $"t:{targetType.Name} {System.IO.Path.GetFileNameWithoutExtension(findTerm)}"; // Search by type and name
string[] guids = AssetDatabase.FindAssets(searchFilter);

if (guids.Length == 1)
{
asset = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(guids[0]), targetType);
if (asset != null) return asset;
}
else if (guids.Length > 1)
{
Debug.LogWarning($"[FindObjectByInstruction] Ambiguous asset find: Found {guids.Length} assets matching filter '{searchFilter}'. Provide a full path or unique name.");
// Optionally return the first one? Or null? Returning null is safer.
return null;
}
// If still not found, fall through to scene search (though unlikely for assets)
}


// --- Scene Object Search ---
// Find the GameObject using the internal finder
GameObject foundGo = FindObjectInternal(new JValue(findTerm), searchMethodToUse);

if (foundGo == null)
{
// Don't warn yet, could still be an asset not found above
// Debug.LogWarning($"Could not find GameObject using instruction: {instruction}");
return null;
}

// Now, get the target object/component from the found GameObject
if (targetType == typeof(GameObject))
{
return foundGo; // We were looking for a GameObject
}
else if (typeof(Component).IsAssignableFrom(targetType))
{
Type componentToGetType = targetType;
if (!string.IsNullOrEmpty(componentName))
{
Type specificCompType = FindType(componentName);
if (specificCompType != null && typeof(Component).IsAssignableFrom(specificCompType))
{
componentToGetType = specificCompType;
}
else
{
Debug.LogWarning($"Could not find component type '{componentName}' specified in find instruction. Falling back to target type '{targetType.Name}'.");
}
}

Component foundComp = foundGo.GetComponent(componentToGetType);
if (foundComp == null)
{
Debug.LogWarning($"Found GameObject '{foundGo.name}' but could not find component of type '{componentToGetType.Name}'.");
}
return foundComp;
}
else
{
Debug.LogWarning($"Find instruction handling not implemented for target type: {targetType.Name}");
return null;
}
}
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 | 🔴 Critical

🧩 Analysis chain

LGTM: Well-structured object finder for deserializer.

The new FindObjectByInstruction method provides a comprehensive search strategy (assets first via AssetDatabase, then scene objects via FindObjectInternal) with good error handling for ambiguous results. The method correctly extracts components when needed.

Verify that UnityEngineObjectConverter correctly calls this method:


🏁 Script executed:

#!/bin/bash
# Find calls to FindObjectByInstruction
rg "FindObjectByInstruction" --type cs -C 3

Length of output: 1541


🏁 Script executed:

#!/bin/bash
# Locate UnityEngineObjectConverter class and check if it invokes FindObjectByInstruction
rg -n "class UnityEngineObjectConverter" -C3 --type cs
rg -n "ConvertJTokenToType" -C3 --type cs

Length of output: 9999


🏁 Script executed:

#!/bin/bash
# Check if FindObjectByInstruction is referenced in the UnityEngineObjectConverter
rg -n "FindObjectByInstruction" -C3 --type cs UnityMcpBridge/Runtime/Serialization/UnityTypeConverters.cs

# Locate and inspect the ReadJson implementation to see how objects are deserialized
rg -n "override .*ReadJson" -C3 --type cs UnityMcpBridge/Runtime/Serialization/UnityTypeConverters.cs

Length of output: 2714


Integrate FindObjectByInstruction into UnityEngineObjectConverter.ReadJson
The new FindObjectByInstruction method is defined but never invoked by UnityEngineObjectConverter. Update the ReadJson implementation in UnityMcpBridge/Runtime/Serialization/UnityTypeConverters.cs (around line 223) to call ManageGameObject.FindObjectByInstruction(jo, objectType) when deserializing UnityEngine.Object.

@msanatan
Copy link
Contributor Author

It's just a whitespace change to avoid PRs being riddled with whitespace changes, making them harder to read. PEP8 and MS C# formatting should be standard in any case

@msanatan msanatan merged commit f6796e6 into CoplayDev:main Sep 30, 2025
1 check passed
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.

1 participant