Skip to content

Conversation

@dsarno
Copy link
Collaborator

@dsarno dsarno commented Sep 1, 2025

Goal: When MCP tools edit C# scripts while Unity is unfocused, the editor compiles and domain‑reloads once in the background, keeping multi‑step edits fast and deterministic.

Summary

Users asked for the MCP to take care of triggering script-compile and domain reload in the background, so the MCP can stack several edits that build on each other, even when they require compilation and assembly reload. Unity is now the single owner of import + compile + domain reload. MCP tools signal intent (immediate vs debounced); the Editor performs the work—so you get one reload per batch and no extra reload when focus returns.

What changed

Unity (package)

  • ManageScript.cs orchestrates reloads:
    • Immediate: synchronous AssetDatabase.ImportAsset(... ForceSynchronousImport | ForceUpdate) + CompilationPipeline.RequestScriptCompilation() (works unfocused).
    • Debounced: coalesces edits, then imports and requests compile once.
  • 'Create script' path uses the same immediate import + request‑compile sequence.
  • Editor/bridge logs now use McpLog and respect “Show Debug Logs” (startup remains always‑on).

Python (MCP server)

  • Tools pass options.refresh and don’t force reloads.
  • Structured edits default to immediate; text/mixed default to debounced.

(Comparison: upstream/main doesn’t provide this single‑owner orchestration; this ensures one reload even when unfocused.)

Behavior

  • Default: debounced batching for most edits; structured edits can use immediate.
  • Immediate: opt‑in; synchronous import + one compile.

Files

  • Unity: UnityMcpBridge/Editor/Tools/ManageScript.cs, Editor/MCPForUnityBridge.cs, Editor/Windows/MCPForUnityEditorWindow.cs
  • Python: UnityMcpServer~/src/tools/manage_script.py, UnityMcpServer~/src/tools/manage_script_edits.py

Test plan

  1. Unfocused, perform multiple edits within ~300–500 ms → one compile/reload.
  2. Immediate edit → synchronous import + one compile.

Notes

The Editor is the single source of truth for compilation & reload timing, reducing cross‑process triggers and eliminating focus‑time extra reloads.

Summary by CodeRabbit

  • New Features

    • Optional “Reload Sentinel” trigger after successful script edits.
    • Centralized, toggleable logging with clearer Info/Warn/Error output.
  • Refactor

    • More robust command handling and I/O framing with timeouts for improved reliability.
    • Better error categorization to reduce noisy logs.
    • Script edits now import/compile immediately when appropriate; debounced refresh used where safer.
    • Menu item execution runs synchronously with a safety blacklist to prevent dangerous actions.
  • Chores

    • Added .gitignore rules to exclude backup artifacts.

…Python writes

- Add MCP/Flip Reload Sentinel editor menu and flip package sentinel synchronously
- Trigger sentinel flip after Create/Update/ApplyTextEdits (sync) in ManageScript
- Instrument flip path with Debug.Log for traceability
- Remove Python reload_sentinel writes; tools now execute Unity menu instead
- Harden reload_sentinel path resolution to project/package
- ExecuteMenuItem runs synchronously for deterministic results
- Verified MCP edits trigger compile/reload without focus; no Python permission errors
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 1, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Caution

Review failed

The pull request is closed.

Walkthrough

Introduces centralized logging, framed I/O, and JSON-based command routing in the Unity MCP bridge. Updates script management to import/compile immediately, adds a new helper API, and adjusts refresh semantics. Adds synchronous menu execution with a blacklist. Extends server-side tools to optionally flip a reload sentinel asynchronously. Minor test assets and .gitignore updates.

Changes

Cohort / File(s) Summary
Bridge core (logging, framing, command routing)
UnityMcpBridge/Editor/MCPForUnityBridge.cs
Replaces direct Debug.Log with gated McpLog, refines error categorization, adds max-size frame protocol, timeout-bound reads/writes, JSON command model with dispatcher and helpers, and cross-version compile checks. Public API unchanged.
Editor tools (menu exec, script management)
UnityMcpBridge/Editor/Tools/ExecuteMenuItem.cs, UnityMcpBridge/Editor/Tools/ManageScript.cs
ExecuteMenuItem now synchronous with blacklist and structured responses; adds placeholder for get_available_menus. ManageScript shifts to immediate import+compile path, updates messages, and adds public ImportAndRequestCompile(relPath, synchronous). Debounced vs immediate refresh clarified.
Editor window (centralized logging)
UnityMcpBridge/Editor/Windows/MCPForUnityEditorWindow.cs
Routes debug logs through McpLog.Info with gating; messages unchanged.
Server tools (sentinel + edit flow)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_script.py, UnityMcpBridge/UnityMcpServer~/src/tools/manage_script_edits.py, UnityMcpBridge/UnityMcpServer~/src/reload_sentinel.py
Adds deprecated no-op reload_sentinel shim. Introduces optional async sentinel flip after successful edits/writes via background thread, with state check and no retries. Defaults some apply_text_edits to debounced refresh; improves regex handling (ignore_case) and errors.
Unity test assets
TestProjects/UnityMCPTests/Assets/Scripts/Hello.cs, .../Hello.cs.meta, TestProjects/UnityMCPTests/Assets/Editor.meta
Cleans up usings and comments in Hello.cs; trims .meta to minimal fields; adds new Editor.meta folder metadata.
Repo housekeeping
.gitignore
Ignores backup artifacts: *.backup and *.backup.meta.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Client
  participant Bridge as MCPForUnityBridge
  participant Router as Command Router
  participant Tools as Tool Handlers
  participant Unity as Unity Editor

  Client->>Bridge: TCP Connect
  Bridge->>Bridge: Handshake (framed)
  loop For each frame
    Client->>Bridge: ReadFrameAsUtf8Async (<=64MB)
    Bridge->>Router: Deserialize + Validate JSON
    alt Valid command
      Router->>Tools: ExecuteCommand(type, params)
      Tools-->>Router: Result {status: success|error, payload}
    else Invalid JSON / unknown
      Router-->>Router: Build error result
    end
    Router-->>Bridge: Serialize result
    Bridge-->>Client: WriteFrameAsync
  end
Loading
sequenceDiagram
  autonumber
  actor MCP as Bridge/Server
  participant Exec as ExecuteMenuItem
  participant Editor as Unity Editor

  MCP->>Exec: HandleCommand(action="execute_menu_item", menu_path)
  Exec->>Exec: Check blacklist / params
  alt Allowed
    Exec->>Editor: EditorApplication.ExecuteMenuItem(menuPath)
    alt Success
      Exec-->>MCP: Response.Success {executed: true}
    else Failure
      Exec-->>MCP: Response.Error {executed: false}
    end
  else Blocked
    Exec-->>MCP: Response.Error {reason: "blacklisted"}
  end
Loading
sequenceDiagram
  autonumber
  actor Server as Python manage_script(_edits)
  participant FS as ~/.unity-mcp/status*.json
  participant Unity as Unity Editor
  participant Menu as MCP/Flip Reload Sentinel

  Server->>Server: Apply edits/write (success)
  alt force_sentinel_reload
    Server-->>Server: Spawn daemon thread (delay)
    Server-->>FS: Read latest status
    alt Not reloading
      Server-->>Unity: execute_menu_item(Menu) (no retries)
    else Already reloading
      Server-->>Server: No-op
    end
  else No flag
    Server-->>Server: No-op
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • msanatan
  • Scriptwonder

Poem

A hop, a skip, I nudge the logs just right,
Frames in neat little coats, zipped snug for flight.
Scripts compile quick—thump-thump goes my heart!
A sentinel flips with the softest start.
Menu paths tread safe, no sudden fright—
Carrot-shaped commits, nibbling through the night. 🥕🐇


📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 0c0ab92 and 092e7f8.

📒 Files selected for processing (1)
  • UnityMcpBridge/UnityMcpServer~/src/tools/manage_script_edits.py (10 hunks)
✨ 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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

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

🧹 Nitpick comments (17)
TestProjects/UnityMCPTests/Assets/Scripts/Hello.cs (1)

2-2: Optional: drop unused using.

If nothing from System.Collections is used, remove to keep usings clean.

-using System.Collections;
UnityMcpBridge/UnityMcpServer~/src/reload_sentinel.py (1)

7-8: Silence Ruff ARG001 and emit a soft deprecation signal.

Rename star-args and add a DeprecationWarning. No side effects; ignored by default.

@@
 
-def flip_reload_sentinel(*args, **kwargs) -> str:
-    return "reload_sentinel.py is deprecated; use execute_menu_item → 'MCP/Flip Reload Sentinel'"
+import warnings
+
+def flip_reload_sentinel(*_args, **_kwargs) -> str:
+    warnings.warn(
+        "reload_sentinel.py is deprecated; use execute_menu_item → 'MCP/Flip Reload Sentinel'",
+        DeprecationWarning,
+        stacklevel=2,
+    )
+    return "reload_sentinel.py is deprecated; use execute_menu_item → 'MCP/Flip Reload Sentinel'"
UnityMcpBridge/UnityMcpServer~/src/tools/manage_script.py (1)

309-340: Make the async sentinel flip more robust and observable (keep it quiet by default).

  • Blind excepts hide actionable failures; add optional debug logging.
  • max_retries=0 risks a lost flip during transient reloads; consider 1–2 polite retries.
@@
-            if resp.get("success") and (options or {}).get("force_sentinel_reload"):
+            if resp.get("success") and (options or {}).get("force_sentinel_reload"):
                 # Optional: flip sentinel via menu if explicitly requested
-                try:
-                    import threading, time, json, glob, os
+                try:
+                    import threading, time, json, glob, os, logging
                     def _latest_status() -> dict | None:
                         try:
                             files = sorted(glob.glob(os.path.expanduser("~/.unity-mcp/unity-mcp-status-*.json")), key=os.path.getmtime, reverse=True)
                             if not files:
                                 return None
                             with open(files[0], "r") as f:
                                 return json.loads(f.read())
-                        except Exception:
+                        except Exception:
                             return None
 
                     def _flip_async():
                         try:
                             time.sleep(0.1)
                             st = _latest_status()
                             if st and st.get("reloading"):
                                 return
-                            send_command_with_retry(
+                            send_command_with_retry(
                                 "execute_menu_item",
                                 {"menuPath": "MCP/Flip Reload Sentinel"},
-                                max_retries=0,
-                                retry_ms=0,
+                                max_retries=2,   # small cushion against transient reloads
+                                retry_ms=200,
                             )
-                        except Exception:
-                            pass
+                        except Exception as e:
+                            if os.environ.get("UNITY_MCP_DEBUG"):
+                                logging.getLogger("unity_mcp").debug("sentinel flip failed: %s", e)
                     threading.Thread(target=_flip_async, daemon=True).start()
                 except Exception:
                     pass
                 return resp
UnityMcpBridge/Editor/Tools/ExecuteMenuItem.cs (2)

42-49: Switch to McpLog for consistent, gated logging.

Use the centralized McpLog wrapper so logs respect “Show Debug Logs” and formatting.

-                        Debug.LogWarning(
-                            "[ExecuteMenuItem] 'get_available_menus' action is not fully implemented. Dynamically listing all menu items is complex."
-                        );
+                        McpLog.Warn("[ExecuteMenuItem] 'get_available_menus' action is not fully implemented. Dynamically listing all menu items is complex.");

-                // Trace incoming execute requests
-                Debug.Log($"[ExecuteMenuItem] Request to execute menu: '{menuPath}'");
+                // Trace incoming execute requests
+                McpLog.Info($"[ExecuteMenuItem] Request to execute menu: '{menuPath}'", always: false);

-                bool executed = EditorApplication.ExecuteMenuItem(menuPath);
+                bool executed = EditorApplication.ExecuteMenuItem(menuPath);
                 if (executed)
                 {
-                    Debug.Log($"[ExecuteMenuItem] Executed successfully: '{menuPath}'");
+                    McpLog.Info($"[ExecuteMenuItem] Executed successfully: '{menuPath}'", always: false);
                     return Response.Success(
                         $"Executed menu item: '{menuPath}'",
                         new { executed = true, menuPath }
                     );
                 }
-                Debug.LogWarning($"[ExecuteMenuItem] Failed (not found/disabled): '{menuPath}'");
+                McpLog.Warn($"[ExecuteMenuItem] Failed (not found/disabled): '{menuPath}'");

-                Debug.LogError($"[ExecuteMenuItem] Error executing '{menuPath}': {e}");
+                McpLog.Error($"[ExecuteMenuItem] Error executing '{menuPath}': {e}");

Also applies to: 99-116, 120-122


30-31: Use culture-invariant normalization and trim inputs.

Prevents edge cases on non-invariant cultures and stray whitespace.

-            string action = @params["action"]?.ToString().ToLower() ?? "execute"; // Default action
+            string action = (@params["action"]?.ToString() ?? "execute").Trim().ToLowerInvariant(); // Default action
...
-            string menuPath = @params["menu_path"]?.ToString() ?? @params["menuPath"]?.ToString();
+            string menuPath = (@params["menu_path"]?.ToString() ?? @params["menuPath"]?.ToString())?.Trim();

Also applies to: 69-71

UnityMcpBridge/UnityMcpServer~/src/tools/manage_script_edits.py (5)

90-99: No-throw is fine, but avoid blind exception swallowing; add gated debug logging.

Keeps diagnostics without affecting control flow.

-    try:
+    try:
         import threading, time
-
         def _flip():
             try:
-                import json, glob, os
+                import json, glob
                 # Small delay so write flushes; prefer early flip to avoid editor-focus second reload
                 time.sleep(0.1)
                 try:
@@
-            except Exception:
-                pass
+            except Exception as ex:
+                if os.environ.get("MCP_DEBUG"):
+                    print(f"[manage_script_edits] sentinel flip error: {ex}")
@@
-    except Exception:
-        pass
+    except Exception as ex:
+        if os.environ.get("MCP_DEBUG"):
+            print(f"[manage_script_edits] sentinel thread error: {ex}")

Also applies to: 114-119


95-96: Remove duplicate os import; use the top-level import.

Minor cleanup.

-                import json, glob, os
+                import json, glob

107-107: Replace the non-breaking hyphen with a standard hyphen.

Prevents lint noise and invisible character issues.

-                # Best‑effort, single-shot; avoid retries during reload window
+                # Best-effort, single-shot; avoid retries during reload window

523-530: Optional: centralize sentinel flip toggle to reduce duplication.

Wrap the repeated “if force_sentinel_reload: _trigger_sentinel_async()” into a helper for readability.

If helpful, I can push a small refactor to DRY this up.

Also applies to: 651-656, 674-679, 801-806, 887-892


99-107: Multi-project safety (follow-up).

You currently pick the most recent status file; in multi-project sessions, consider preferring the file whose unity_port matches the active connection (read JSON and compare). Not blocking, but worth a TODO.

UnityMcpBridge/Editor/MCPForUnityBridge.cs (3)

233-236: Route editor logs through McpLog for consistency.

Keep the gating; just switch to McpLog for uniform formatting.

-                        Debug.Log($"<b><color=#2EA3FF>MCP-FOR-UNITY</color></b>: MCPForUnityBridge already running on port {currentUnityPort}");
+                        McpLog.Info($"MCPForUnityBridge already running on port {currentUnityPort}", always: false);
...
-                    if (IsDebugEnabled()) Debug.Log("<b><color=#2EA3FF>MCP-FOR-UNITY</color></b>: MCPForUnityBridge stopped.");
+                    if (IsDebugEnabled()) McpLog.Info("MCPForUnityBridge stopped.", always: false);
...
-                        Debug.Log($"<b><color=#2EA3FF>UNITY-MCP</color></b>: Client connected {ep}");
+                        McpLog.Info($"Client connected {ep}", always: false);

Also applies to: 355-355, 409-413


336-361: Clear commandQueue on Stop to avoid stale tasks after reload.

Prevents lingering TCS references if Stop() triggers mid-flight.

                     listener?.Stop();
                     listener = null;
                     EditorApplication.update -= ProcessCommands;
+                    // Drop any queued items to free TCS and avoid leaks across reloads
+                    lock (lockObj) { commandQueue.Clear(); }
                     if (IsDebugEnabled()) Debug.Log("<b><color=#2EA3FF>MCP-FOR-UNITY</color></b>: MCPForUnityBridge stopped.");

392-397: Prefer Warn over Error for listener loop noise when debug-enabled.

These are often transient during reload; align severity with your benign/real split below.

-                        if (IsDebugEnabled()) Debug.LogError($"Listener error: {ex.Message}");
+                        if (IsDebugEnabled()) McpLog.Warn($"Listener: {ex.Message}");
UnityMcpBridge/Editor/Tools/ManageScript.cs (4)

359-369: Immediate import/compile on create: add resilience (fallback) and confirm reload timing

The immediate path is aligned with the PR goals. To avoid a failed import/compile (e.g., transient AssetDatabase exceptions) leaving the system without any reload, wrap in try/catch and fall back to the debounced scheduler. Also, please sanity‑check that your RPC reply returns before any domain reload kicks in on your target Unity versions.

-                // Perform synchronous import/compile to ensure immediate reload
-                AssetDatabase.ImportAsset(
-                    relativePath,
-                    ImportAssetOptions.ForceSynchronousImport | ImportAssetOptions.ForceUpdate
-                );
-#if UNITY_EDITOR
-                UnityEditor.Compilation.CompilationPipeline.RequestScriptCompilation();
-#endif
+                // Perform synchronous import/compile to ensure immediate reload
+                try
+                {
+                    AssetDatabase.ImportAsset(
+                        relativePath,
+                        ImportAssetOptions.ForceSynchronousImport | ImportAssetOptions.ForceUpdate
+                    );
+#if UNITY_EDITOR
+                    UnityEditor.Compilation.CompilationPipeline.RequestScriptCompilation();
+#endif
+                }
+                catch (Exception ex)
+                {
+                    Debug.LogWarning($"[ManageScript] Immediate refresh after create failed for '{relativePath}': {ex.Message}. Falling back to debounced.");
+                    ManageScriptRefreshHelpers.ScheduleScriptRefresh(relativePath);
+                }

660-660: Overlap check: simplify for readability

The new expression is equivalent to using the stored end index and slightly harder to parse at a glance. Prefer the direct property for clarity.

-                if (spans[i].start + (spans[i].end - spans[i].start) > spans[i - 1].start)
+                if (spans[i].end > spans[i - 1].start)

773-780: Immediate edit path: add fallback and align logging with centralized gate

  • Add try/catch + debounced fallback to avoid dropping reloads on exceptions.
  • Consider switching Debug.Log to the centralized logger (e.g., McpLog.Debug) to respect “Show Debug Logs”.
-                    Debug.Log($"[ManageScript] ApplyTextEdits: immediate refresh for '{relativePath}'");
-                    AssetDatabase.ImportAsset(
-                        relativePath,
-                        ImportAssetOptions.ForceSynchronousImport | ImportAssetOptions.ForceUpdate
-                    );
-#if UNITY_EDITOR
-                    UnityEditor.Compilation.CompilationPipeline.RequestScriptCompilation();
-#endif
+                    Debug.Log($"[ManageScript] ApplyTextEdits: immediate refresh for '{relativePath}'");
+                    try
+                    {
+                        AssetDatabase.ImportAsset(
+                            relativePath,
+                            ImportAssetOptions.ForceSynchronousImport | ImportAssetOptions.ForceUpdate
+                        );
+#if UNITY_EDITOR
+                        UnityEditor.Compilation.CompilationPipeline.RequestScriptCompilation();
+#endif
+                    }
+                    catch (Exception ex)
+                    {
+                        Debug.LogWarning($"[ManageScript] Immediate refresh failed for '{relativePath}': {ex.Message}. Falling back to debounced.");
+                        ManageScriptRefreshHelpers.ScheduleScriptRefresh(relativePath);
+                    }

If available in your codebase, replace the two Debug.Log calls here with the gated logger (example):

  • McpLog.Debug($"[ManageScript] ApplyTextEdits: immediate refresh for '{relativePath}'");
  • McpLog.Debug($"[ManageScript] ApplyTextEdits: debounced refresh scheduled for '{relativePath}'");

Also applies to: 785-785


1430-1437: Structured edits immediate path: add try/catch + debounced fallback

Same resilience rationale as the other immediate paths.

-                    // Perform synchronous import/compile immediately to ensure reload even when Editor is unfocused
-                    AssetDatabase.ImportAsset(
-                        relativePath,
-                        ImportAssetOptions.ForceSynchronousImport | ImportAssetOptions.ForceUpdate
-                    );
-#if UNITY_EDITOR
-                    UnityEditor.Compilation.CompilationPipeline.RequestScriptCompilation();
-#endif
+                    // Perform synchronous import/compile immediately to ensure reload even when Editor is unfocused
+                    try
+                    {
+                        AssetDatabase.ImportAsset(
+                            relativePath,
+                            ImportAssetOptions.ForceSynchronousImport | ImportAssetOptions.ForceUpdate
+                        );
+#if UNITY_EDITOR
+                        UnityEditor.Compilation.CompilationPipeline.RequestScriptCompilation();
+#endif
+                    }
+                    catch (Exception ex)
+                    {
+                        Debug.LogWarning($"[ManageScript] Immediate refresh after structured edit failed for '{relativePath}': {ex.Message}. Falling back to debounced.");
+                        ManageScriptRefreshHelpers.ScheduleScriptRefresh(relativePath);
+                    }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between ad848f0 and 903c804.

⛔ Files ignored due to path filters (1)
  • UnityMcpBridge/UnityMcpServer~/src/uv.lock is excluded by !**/*.lock
📒 Files selected for processing (14)
  • TestProjects/UnityMCPTests/Assets/Editor.meta (1 hunks)
  • TestProjects/UnityMCPTests/Assets/Scripts/Hello.cs (1 hunks)
  • TestProjects/UnityMCPTests/Assets/Scripts/Hello.cs.meta (1 hunks)
  • UnityMcpBridge/Editor/MCPForUnityBridge.cs (8 hunks)
  • UnityMcpBridge/Editor/Sentinel.meta (1 hunks)
  • UnityMcpBridge/Editor/Sentinel/__McpReloadSentinel.cs (1 hunks)
  • UnityMcpBridge/Editor/Sentinel/__McpReloadSentinel.cs.meta (1 hunks)
  • UnityMcpBridge/Editor/Tools/ExecuteMenuItem.cs (2 hunks)
  • UnityMcpBridge/Editor/Tools/ManageScript.cs (5 hunks)
  • UnityMcpBridge/Editor/Tools/ManageScript.cs.backup.meta (1 hunks)
  • UnityMcpBridge/Editor/Windows/MCPForUnityEditorWindow.cs (2 hunks)
  • UnityMcpBridge/UnityMcpServer~/src/reload_sentinel.py (1 hunks)
  • UnityMcpBridge/UnityMcpServer~/src/tools/manage_script.py (2 hunks)
  • UnityMcpBridge/UnityMcpServer~/src/tools/manage_script_edits.py (9 hunks)
🧰 Additional context used
🧬 Code graph analysis (8)
UnityMcpBridge/Editor/Windows/MCPForUnityEditorWindow.cs (1)
UnityMcpBridge/Editor/Helpers/McpLog.cs (2)
  • McpLog (6-30)
  • Info (15-19)
UnityMcpBridge/Editor/Sentinel/__McpReloadSentinel.cs (1)
UnityMcpBridge/Editor/Tools/ManageScript.cs (1)
  • Tick (2585-2617)
UnityMcpBridge/Editor/Tools/ExecuteMenuItem.cs (1)
UnityMcpBridge/Editor/Helpers/Response.cs (3)
  • Response (10-61)
  • Success (18-33)
  • Error (41-60)
UnityMcpBridge/Editor/Tools/ManageScript.cs (1)
UnityMcpBridge/Editor/Helpers/Response.cs (2)
  • Response (10-61)
  • Error (41-60)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_script.py (1)
UnityMcpBridge/UnityMcpServer~/src/unity_connection.py (1)
  • send_command_with_retry (388-407)
UnityMcpBridge/Editor/MCPForUnityBridge.cs (2)
UnityMcpBridge/Editor/Helpers/McpLog.cs (3)
  • McpLog (6-30)
  • Info (15-19)
  • Warn (21-24)
UnityMcpBridge/Editor/Helpers/PortManager.cs (1)
  • IsDebugEnabled (19-23)
TestProjects/UnityMCPTests/Assets/Scripts/Hello.cs (1)
UnityMcpBridge/Editor/Tools/ManageScript.cs (1)
  • System (2279-2345)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_script_edits.py (1)
UnityMcpBridge/UnityMcpServer~/src/unity_connection.py (1)
  • send_command_with_retry (388-407)
🪛 Ruff (0.12.2)
UnityMcpBridge/UnityMcpServer~/src/reload_sentinel.py

7-7: Unused function argument: args

(ARG001)


7-7: Unused function argument: kwargs

(ARG001)

UnityMcpBridge/UnityMcpServer~/src/tools/manage_script.py

320-320: Do not catch blind exception: Exception

(BLE001)


335-336: try-except-pass detected, consider logging the exception

(S110)


335-335: Do not catch blind exception: Exception

(BLE001)


338-339: try-except-pass detected, consider logging the exception

(S110)


338-338: Do not catch blind exception: Exception

(BLE001)

UnityMcpBridge/UnityMcpServer~/src/tools/manage_script_edits.py

105-106: try-except-pass detected, consider logging the exception

(S110)


105-105: Do not catch blind exception: Exception

(BLE001)


107-107: Comment contains ambiguous (NON-BREAKING HYPHEN). Did you mean - (HYPHEN-MINUS)?

(RUF003)


114-115: try-except-pass detected, consider logging the exception

(S110)


114-114: Do not catch blind exception: Exception

(BLE001)


118-119: try-except-pass detected, consider logging the exception

(S110)


118-118: Do not catch blind exception: Exception

(BLE001)


528-529: try-except-pass detected, consider logging the exception

(S110)


528-528: Do not catch blind exception: Exception

(BLE001)


654-655: try-except-pass detected, consider logging the exception

(S110)


654-654: Do not catch blind exception: Exception

(BLE001)


656-656: Do not catch blind exception: Exception

(BLE001)


677-678: try-except-pass detected, consider logging the exception

(S110)


677-677: Do not catch blind exception: Exception

(BLE001)


804-805: try-except-pass detected, consider logging the exception

(S110)


804-804: Do not catch blind exception: Exception

(BLE001)


890-891: try-except-pass detected, consider logging the exception

(S110)


890-890: Do not catch blind exception: Exception

(BLE001)

🔇 Additional comments (11)
UnityMcpBridge/Editor/Sentinel/__McpReloadSentinel.cs.meta (1)

1-2: LGTM: Sentinel meta looks correct.

GUID pinned; good to have this under Editor-only. No action.

TestProjects/UnityMCPTests/Assets/Scripts/Hello.cs.meta (1)

1-2: MonoImporter missing in multiple .cs.meta files—confirm Unity version or regenerate

Missing MonoImporter in:

  • TestProjects/UnityMCPTests/Assets/Scripts/Hello.cs.meta
  • TestProjects/UnityMCPTests/Assets/Scripts/LongUnityScriptClaudeTest.cs.meta
  • UnityMcpBridge/Editor/Helpers/PackageDetector.cs.meta
  • UnityMcpBridge/Editor/Sentinel/__McpReloadSentinel.cs.meta

Verify your Unity editor version preserves minimal meta headers without a MonoImporter section; if it doesn’t, reimport these scripts so Unity adds the MonoImporter blocks and commit the updated .meta files.

TestProjects/UnityMCPTests/Assets/Editor.meta (1)

1-8: LGTM: Standard Editor folder meta.

Matches expected structure.

TestProjects/UnityMCPTests/Assets/Scripts/Hello.cs (1)

6-15: LGTM: No functional changes; harmless comment/whitespace tweaks.

Safe for tests/example scene.

UnityMcpBridge/Editor/Sentinel/__McpReloadSentinel.cs (1)

1-10: Editor-only sentinel looks good; minimal and safe.

Internal, UNITY_EDITOR-gated, and toggling a const for IL churn is fine.

UnityMcpBridge/Editor/Sentinel.meta (1)

1-8: Meta asset is correct.

GUID and folderAsset entries look standard.

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

1774-1775: Good switch to McpLog with debug gating.

Using McpLog.Info(..., always: false) aligns with the central logger policy.


1974-1975: Consistent logging to McpLog.

Nice move off Debug.Log; respects the "Show Debug Logs" toggle.

UnityMcpBridge/UnityMcpServer~/src/tools/manage_script.py (1)

492-493: Debouncing legacy ‘update’ path is the right call.

Routing to apply_text_edits with refresh="debounced" avoids extra compiles/reloads.

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

17-23: Implement allowlisting for menu items
Replace the blacklist-only check with combined blacklist + allowlist logic to minimize blast radius:

 private static readonly HashSet<string> _menuPathBlacklist = new HashSet<string>(
     StringComparer.OrdinalIgnoreCase
 )
 {
     "File/Quit",
 };

+// Only commands under these prefixes are permitted
+private static readonly string[] _allowedPrefixes = new[]
+{
+    "MCP/",
+    // add any other safe prefixes, e.g. "Assets/", "GameObject/"
+};- if (_menuPathBlacklist.Contains(menuPath))
+ if (_menuPathBlacklist.Contains(menuPath)
+     || !_allowedPrefixes.Any(p => menuPath.StartsWith(p, StringComparison.OrdinalIgnoreCase)))
 {
     return Response.Error(
         $"Execution of menu item '{menuPath}' is blocked for safety reasons."
     );
 }

Verify that every existing caller of ExecuteMenuItem only uses menu paths under these prefixes (add to _allowedPrefixes as needed).

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

1335-1336: Consistent overlap-conflict hint

Hint text aligns with the non-structured path and is clear/actionable. LGTM.

@dsarno dsarno requested a review from Copilot September 1, 2025 21:52
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR implements automatic background compilation and domain reload for MCP script edits and new script creation. The goal is to ensure Unity compiles and reloads scripts in the background when the editor is unfocused, enabling multi-step edits that build on each other while maintaining fast and deterministic behavior.

Key changes:

  • Centralized Unity as the single owner of import, compile, and domain reload operations
  • Added immediate vs. debounced refresh modes with structured edits defaulting to immediate and text/mixed edits defaulting to debounced
  • Replaced external sentinel file manipulation with Unity menu-based reload triggering

Reviewed Changes

Copilot reviewed 14 out of 16 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
manage_script_edits.py Added sentinel triggering logic and changed default refresh mode from immediate to debounced
manage_script.py Updated default refresh mode and added sentinel triggering for successful operations
reload_sentinel.py Created compatibility shim deprecating external sentinel manipulation
MCPForUnityEditorWindow.cs Replaced direct Debug.Log calls with McpLog.Info for consistent logging
ManageScript.cs Implemented synchronous import/compile for immediate refresh and fixed overlap detection logic
ExecuteMenuItem.cs Changed from asynchronous to synchronous execution with improved error handling
__McpReloadSentinel.cs Added editor-only sentinel class for assembly reload detection
MCPForUnityBridge.cs Enhanced logging with McpLog and improved error categorization
Hello.cs Test file modifications for validation
Comments suppressed due to low confidence (1)

UnityMcpBridge/UnityMcpServer~/src/tools/manage_script_edits.py:1

  • The expression spans[i].start + (spans[i].end - spans[i].start) is equivalent to spans[i].end. This should be simplified to if spans[i].end > spans[i - 1].start: for better readability.
from mcp.server.fastmcp import FastMCP, Context

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

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

🧹 Nitpick comments (6)
.gitignore (1)

39-42: Include additional backup/temp ignore patterns

Optionally ignore more common backup and temp files to reduce stray churn:

 # Backup artifacts
 *.backup
 *.backup.meta
+*.bak
+*.bak.meta
+*.orig
+*.orig.meta
+*.tmp
+*~

Verified no existing tracked files match these patterns.

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

359-370: Create: immediate import+compile — verify reply isn’t preempted; consider DRY helper

Calling RequestScriptCompilation before returning could trigger a very fast compile and domain reload; please verify the response reliably reaches the caller under unfocused editor loads. Also, this import+compile sequence is duplicated in several places—prefer a single helper to avoid drift.

Apply this refactor locally:

-                // Perform synchronous import/compile to ensure immediate reload
-                AssetDatabase.ImportAsset(
-                    relativePath,
-                    ImportAssetOptions.ForceSynchronousImport | ImportAssetOptions.ForceUpdate
-                );
-#if UNITY_EDITOR
-                UnityEditor.Compilation.CompilationPipeline.RequestScriptCompilation();
-#endif
+                ManageScriptRefreshHelpers.ImportAndRequestCompile(relativePath);

Add this helper (outside the shown range):

static class ManageScriptRefreshHelpers
{
    public static void ImportAndRequestCompile(string relPath, bool synchronous = true)
    {
        var opts = ImportAssetOptions.ForceUpdate;
        if (synchronous) opts |= ImportAssetOptions.ForceSynchronousImport;
        AssetDatabase.ImportAsset(relPath, opts);
#if UNITY_EDITOR
        CompilationPipeline.RequestScriptCompilation();
#endif
    }
}

773-781: Gate info logs behind the “Show Debug Logs” toggle; reuse the helper to DRY import+compile

McpLog.Info defaults to always=true. Pass always:false so these messages respect the user’s debug setting. Also, prefer the shared helper for the import+compile sequence.

-                    McpLog.Info($"[ManageScript] ApplyTextEdits: immediate refresh for '{relativePath}'");
-                    AssetDatabase.ImportAsset(
-                        relativePath,
-                        ImportAssetOptions.ForceSynchronousImport | ImportAssetOptions.ForceUpdate
-                    );
-#if UNITY_EDITOR
-                    UnityEditor.Compilation.CompilationPipeline.RequestScriptCompilation();
-#endif
+                    McpLog.Info($"[ManageScript] ApplyTextEdits: immediate refresh for '{relativePath}'", always: false);
+                    ManageScriptRefreshHelpers.ImportAndRequestCompile(relativePath);
...
-                    McpLog.Info($"[ManageScript] ApplyTextEdits: debounced refresh scheduled for '{relativePath}'");
+                    McpLog.Info($"[ManageScript] ApplyTextEdits: debounced refresh scheduled for '{relativePath}'", always: false);

Also applies to: 785-785


788-797: Add scheduledRefresh to response for consistency with other endpoints

Other paths include this flag; helpful for clients to reason about reload behavior.

                 return Response.Success(
                     $"Applied {spans.Count} text edit(s) to '{relativePath}'.",
                     new
                     {
                         uri = $"unity://path/{relativePath}",
                         path = relativePath,
                         editsApplied = spans.Count,
-                        sha256 = newSha
+                        sha256 = newSha,
+                        scheduledRefresh = !immediate
                     }
                 );

1430-1437: Unify immediate import+compile; optionally add gated info log

Same DRY opportunity here; also consider a gated info to mirror ApplyTextEdits.

-                    // Perform synchronous import/compile immediately to ensure reload even when Editor is unfocused
-                    AssetDatabase.ImportAsset(
-                        relativePath,
-                        ImportAssetOptions.ForceSynchronousImport | ImportAssetOptions.ForceUpdate
-                    );
-#if UNITY_EDITOR
-                    UnityEditor.Compilation.CompilationPipeline.RequestScriptCompilation();
-#endif
+                    // Ensure reload even when Editor is unfocused
+                    McpLog.Info($"[ManageScript] EditScript: immediate refresh for '{relativePath}'", always: false);
+                    ManageScriptRefreshHelpers.ImportAndRequestCompile(relativePath);

196-201: Prefer McpLog for deprecation notices for consistency

Align these warnings with the standardized logging.

-                    Debug.LogWarning("manage_script.read is deprecated; prefer resources/read. Serving read for backward compatibility.");
+                    McpLog.Warn("manage_script.read is deprecated; prefer resources/read. Serving read for backward compatibility.");
-                    Debug.LogWarning("manage_script.update is deprecated; prefer apply_text_edits. Serving update for backward compatibility.");
+                    McpLog.Warn("manage_script.update is deprecated; prefer apply_text_edits. Serving update for backward compatibility.");
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 903c804 and 4d04aaa.

📒 Files selected for processing (2)
  • .gitignore (1 hunks)
  • UnityMcpBridge/Editor/Tools/ManageScript.cs (4 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
UnityMcpBridge/Editor/Tools/ManageScript.cs (1)
UnityMcpBridge/Editor/Helpers/McpLog.cs (2)
  • McpLog (6-30)
  • Info (15-19)
🔇 Additional comments (2)
UnityMcpBridge/Editor/Tools/ManageScript.cs (2)

1335-1336: LGTM: clearer overlap hint

The hint now correctly references “same snapshot.” Looks good.


2624-2625: Debounce window is 200 ms; PR/test plan targets ~300–500 ms

To better match the stated behavior (one reload for edits within ~300–500 ms), consider widening the window. Suggest 350 ms pending verification in your environment.

-        RefreshDebounce.Schedule(relPath, TimeSpan.FromMilliseconds(200));
+        RefreshDebounce.Schedule(relPath, TimeSpan.FromMilliseconds(350));

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

♻️ Duplicate comments (1)
UnityMcpBridge/Editor/Tools/ManageScript.cs (1)

660-660: Overlap check simplification is correct and clearer

spans[i].end > spans[i - 1].start is the right, readable condition. This addresses the prior feedback.

🧹 Nitpick comments (5)
UnityMcpBridge/Editor/Tools/ManageScript.cs (5)

362-370: Create: add debug-gated log and exception safety around import/compile

Log this path via McpLog (debug-gated) and guard the import/compile to avoid aborting the reply if Unity throws.

-// Perform synchronous import/compile to ensure immediate reload
-AssetDatabase.ImportAsset(
-    relativePath,
-    ImportAssetOptions.ForceSynchronousImport | ImportAssetOptions.ForceUpdate
-);
-#if UNITY_EDITOR
-UnityEditor.Compilation.CompilationPipeline.RequestScriptCompilation();
-#endif
+// Perform synchronous import/compile to ensure immediate reload
+try
+{
+    McpLog.Info($"[ManageScript] CreateScript: immediate import/compile for '{relativePath}'", always: false);
+    AssetDatabase.ImportAsset(
+        relativePath,
+        ImportAssetOptions.ForceSynchronousImport | ImportAssetOptions.ForceUpdate
+    );
+#if UNITY_EDITOR
+    UnityEditor.Compilation.CompilationPipeline.RequestScriptCompilation();
+#endif
+}
+catch (Exception ex)
+{
+    McpLog.Warn($"[ManageScript] CreateScript: import/compile failed for '{relativePath}': {ex.Message}");
+}

773-781: Immediate text edits: respect debug gating and harden the import/compile

Pass always: false so Info obeys “Show Debug Logs,” and catch exceptions to avoid interrupting the request flow.

-    McpLog.Info($"[ManageScript] ApplyTextEdits: immediate refresh for '{relativePath}'");
-    AssetDatabase.ImportAsset(
-        relativePath,
-        ImportAssetOptions.ForceSynchronousImport | ImportAssetOptions.ForceUpdate
-    );
-#if UNITY_EDITOR
-    UnityEditor.Compilation.CompilationPipeline.RequestScriptCompilation();
-#endif
+    try
+    {
+        McpLog.Info($"[ManageScript] ApplyTextEdits: immediate refresh for '{relativePath}'", always: false);
+        AssetDatabase.ImportAsset(
+            relativePath,
+            ImportAssetOptions.ForceSynchronousImport | ImportAssetOptions.ForceUpdate
+        );
+#if UNITY_EDITOR
+        UnityEditor.Compilation.CompilationPipeline.RequestScriptCompilation();
+#endif
+    }
+    catch (Exception ex)
+    {
+        McpLog.Warn($"[ManageScript] ApplyTextEdits: immediate import/compile failed for '{relativePath}': {ex.Message}");
+    }

785-785: Debounced text edits: gate the info log under “Show Debug Logs”

Use always: false so routine debounced notices don’t spam consoles.

-    McpLog.Info($"[ManageScript] ApplyTextEdits: debounced refresh scheduled for '{relativePath}'");
+    McpLog.Info($"[ManageScript] ApplyTextEdits: debounced refresh scheduled for '{relativePath}'", always: false);

1430-1437: Structured edits: add debug-gated log and try/catch around immediate import/compile

Mirror the immediate text-edits path for consistency and resilience.

-// Perform synchronous import/compile immediately to ensure reload even when Editor is unfocused
-AssetDatabase.ImportAsset(
-    relativePath,
-    ImportAssetOptions.ForceSynchronousImport | ImportAssetOptions.ForceUpdate
-);
-#if UNITY_EDITOR
-UnityEditor.Compilation.CompilationPipeline.RequestScriptCompilation();
-#endif
+// Perform synchronous import/compile immediately to ensure reload even when Editor is unfocused
+try
+{
+    McpLog.Info($"[ManageScript] EditScript: immediate import/compile for '{relativePath}'", always: false);
+    AssetDatabase.ImportAsset(
+        relativePath,
+        ImportAssetOptions.ForceSynchronousImport | ImportAssetOptions.ForceUpdate
+    );
+#if UNITY_EDITOR
+    UnityEditor.Compilation.CompilationPipeline.RequestScriptCompilation();
+#endif
+}
+catch (Exception ex)
+{
+    McpLog.Warn($"[ManageScript] EditScript: immediate import/compile failed for '{relativePath}': {ex.Message}");
+}

2620-2626: DRY the immediate import/compile into a helper for parity and single-point tuning

You repeat the exact import/compile sequence in three places. Centralize it in ManageScriptRefreshHelpers so future tweaks (flags, logging, metrics) are one-line changes.

Add this helper (outside the diffed ranges):

internal static class ManageScriptRefreshHelpers
{
    public static void ForceImportAndCompile(string relPath, bool verbose = false)
    {
        try
        {
            if (verbose) McpLog.Info($"[ManageScript] Force import/compile: '{relPath}'", always: false);
            AssetDatabase.ImportAsset(relPath, ImportAssetOptions.ForceSynchronousImport | ImportAssetOptions.ForceUpdate);
#if UNITY_EDITOR
            UnityEditor.Compilation.CompilationPipeline.RequestScriptCompilation();
#endif
        }
        catch (Exception ex)
        {
            McpLog.Warn($"[ManageScript] ForceImportAndCompile failed for '{relPath}': {ex.Message}");
        }
    }
}

Then replace the three callsites with ManageScriptRefreshHelpers.ForceImportAndCompile(relativePath, verbose: true).

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 4d04aaa and ebd6bc1.

📒 Files selected for processing (1)
  • UnityMcpBridge/Editor/Tools/ManageScript.cs (5 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
UnityMcpBridge/Editor/Tools/ManageScript.cs (2)
UnityMcpBridge/Editor/Helpers/McpLog.cs (2)
  • McpLog (6-30)
  • Info (15-19)
UnityMcpBridge/Editor/Helpers/Response.cs (2)
  • Response (10-61)
  • Error (41-60)
🔇 Additional comments (2)
UnityMcpBridge/Editor/Tools/ManageScript.cs (2)

359-360: Create response flag aligns with immediate reload path

Setting scheduledRefresh = false is consistent with the new synchronous import/compile flow after create.


1335-1336: Consistent conflict hint message

Switch to “same snapshot” keeps overlap messaging consistent across flows.

…portAndRequestCompile; replace duplicated sequences
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: 0

♻️ Duplicate comments (1)
UnityMcpBridge/Editor/Tools/ManageScript.cs (1)

653-657: Overlap check simplification resolved prior feedback

The condition now directly compares spans[i].end > spans[i - 1].start, which is clearer and correct.

🧹 Nitpick comments (5)
UnityMcpBridge/Editor/Tools/ManageScript.cs (5)

359-363: Create-script: immediate import+compile looks correct

Returning a response before triggering a synchronous import and compile is the right call to avoid blocking the transport. Consider adding a gated info log for traceability.

Apply this diff to add a debug-gated log:

                 var ok = Response.Success(
                     $"Script '{name}.cs' created successfully at '{relativePath}'.",
                     new { uri, scheduledRefresh = false }
                 );

+                McpLog.Info($"[ManageScript] CreateScript: immediate import+compile for '{relativePath}'", always: false);
                 ManageScriptRefreshHelpers.ImportAndRequestCompile(relativePath);

766-773: Gate info logs behind “Show Debug Logs” and keep them non-spammy

McpLog.Info defaults to always = true; pass always: false so these messages respect the editor toggle.

Apply this diff:

-                    McpLog.Info($"[ManageScript] ApplyTextEdits: immediate refresh for '{relativePath}'");
+                    McpLog.Info($"[ManageScript] ApplyTextEdits: immediate refresh for '{relativePath}'", always: false);

776-779: Debounced-path log should also be debug-gated

Same rationale: respect the user’s log preference.

Apply this diff:

-                    McpLog.Info($"[ManageScript] ApplyTextEdits: debounced refresh scheduled for '{relativePath}'");
+                    McpLog.Info($"[ManageScript] ApplyTextEdits: debounced refresh scheduled for '{relativePath}'", always: false);

2608-2612: Debounce window: align with test plan or make configurable

PR plan cites ~300–500 ms; code uses 200 ms. Consider bumping to 300 ms or making it EditorPrefs-driven.

Apply this diff to match 300 ms:

-        RefreshDebounce.Schedule(relPath, TimeSpan.FromMilliseconds(200));
+        RefreshDebounce.Schedule(relPath, TimeSpan.FromMilliseconds(300));

2613-2621: New helper: add guard, debug-gated trace, and consider namespace alignment

  • Guard against null/empty paths and add a gated trace for diagnostics.
  • Optional: move ManageScriptRefreshHelpers (and RefreshDebounce) into MCPForUnity.Editor.Tools for consistency; they’re currently in the global namespace.

Apply this diff to harden and add trace:

     public static void ImportAndRequestCompile(string relPath, bool synchronous = true)
     {
-        var opts = ImportAssetOptions.ForceUpdate;
+        if (string.IsNullOrEmpty(relPath))
+        {
+            McpLog.Warn("ImportAndRequestCompile called with null/empty path");
+            return;
+        }
+        var opts = ImportAssetOptions.ForceUpdate;
         if (synchronous) opts |= ImportAssetOptions.ForceSynchronousImport;
+        McpLog.Info($"[ManageScript] ImportAndRequestCompile('{relPath}'): opts={opts}", always: false);
         AssetDatabase.ImportAsset(relPath, opts);
 #if UNITY_EDITOR
         UnityEditor.Compilation.CompilationPipeline.RequestScriptCompilation();
 #endif
     }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between ebd6bc1 and 9a8b1f0.

📒 Files selected for processing (1)
  • UnityMcpBridge/Editor/Tools/ManageScript.cs (6 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
UnityMcpBridge/Editor/Tools/ManageScript.cs (2)
UnityMcpBridge/Editor/Helpers/McpLog.cs (2)
  • McpLog (6-30)
  • Info (15-19)
UnityMcpBridge/Editor/Helpers/Response.cs (2)
  • Response (10-61)
  • Error (41-60)
🔇 Additional comments (2)
UnityMcpBridge/Editor/Tools/ManageScript.cs (2)

1325-1329: Consistent “same snapshot” hint

The overlap error hint is now consistent with prior terminology. Good improvement for operator clarity.


1421-1424: Structured edits: immediate import+compile via helper is appropriate

Delegating to the new helper aligns immediate structured edits with the create path and ensures reload when unfocused.

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

Caution

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

⚠️ Outside diff range comments (1)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_script_edits.py (1)

699-707: Bug: ignore_case is not honored for anchor_insert on the text path.

In this branch, the compiled regex omits IGNORECASE, unlike the mixed-path implementation above that respects e.get("ignore_case"). This can change behavior for callers expecting case-insensitive anchors.

Proposed fix (outside this hunk; adjust locally):

flags = _re.MULTILINE | (_re.IGNORECASE if e.get("ignore_case") else 0)
regex_obj = _re.compile(anchor, flags)
♻️ Duplicate comments (4)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_script_edits.py (4)

84-114: Reload sentinel helper: honor UNITY_MCP_STATUS_DIR, avoid silent failures, and align doc with behavior.

  • The helper currently does nothing beyond a status read; the docstring promises a “menu flip.” Either implement the flip or update the doc to say it’s a no-op “status probe.” Also, avoid swallowing all exceptions silently and fix the NBSP hyphen in the comment.
  • Per prior review, honor UNITY_MCP_STATUS_DIR when locating the status file.

Apply this diff in-place:

 def _trigger_sentinel_async() -> None:
     """Fire the Unity menu flip on a short-lived background thread.
 
     This avoids blocking the current request or getting stuck during domain reloads
     (socket reconnects) when the Editor recompiles.
     """
     try:
-        import threading, time
+        import threading, time
 
         def _flip():
             try:
-                import json, glob, os
+                import json, glob, os, logging
                 # Small delay so write flushes; prefer early flip to avoid editor-focus second reload
                 time.sleep(0.1)
                 try:
-                    files = sorted(glob.glob(os.path.expanduser("~/.unity-mcp/unity-mcp-status-*.json")), key=os.path.getmtime, reverse=True)
+                    status_dir = os.environ.get("UNITY_MCP_STATUS_DIR") or os.path.join(os.path.expanduser("~"), ".unity-mcp")
+                    files = sorted(
+                        glob.glob(os.path.join(status_dir, "unity-mcp-status-*.json")),
+                        key=os.path.getmtime,
+                        reverse=True
+                    )
                     if files:
                         with open(files[0], "r") as f:
                             st = json.loads(f.read())
                             if st.get("reloading"):
                                 return
-                except Exception:
-                    pass
-                # Removed best‑effort menu flip; rely on import/compile triggers instead
-            except Exception:
-                pass
+                except Exception:
+                    logging.getLogger(__name__).debug("Sentinel status scan failed", exc_info=True)
+                # Removed best-effort menu flip; rely on import/compile triggers instead
+            except Exception:
+                logging.getLogger(__name__).debug("Sentinel thread error", exc_info=True)
 
         threading.Thread(target=_flip, daemon=True).start()
-    except Exception:
-        pass
+    except Exception:
+        import logging
+        logging.getLogger(__name__).debug("Failed starting sentinel thread", exc_info=True)

Optional follow-up (pick one):

  • If intentional no-op: rename to _probe_sentinel_async and update the docstring accordingly.
  • If a flip is still desired: wire a dedicated Unity bridge command (e.g., “pulse_reload_sentinel”) and invoke it here with send_command_with_retry.

667-673: Same sentinel concern as above.

Until the helper actually flips, consider removing or no-op-gating these calls.


794-800: Same sentinel concern as above.

Apply the earlier decision here too.


880-886: Same sentinel concern as above.

Align with whatever you decide for the helper (implement or remove).

🧹 Nitpick comments (2)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_script_edits.py (2)

5-5: Add a module logger for selective debug traces.

You’re adding I/O that can fail; set up a module logger once and use debug-level logs instead of silent pass.

Example (outside this hunk, near the imports):

import logging
logger = logging.getLogger(__name__)

517-524: Re-evaluate sentinel toggle calls.

Given _trigger_sentinel_async currently performs no flip, these guarded calls add overhead without benefit. Either implement the flip (preferred) or remove these gated calls and the force_sentinel_reload option for now.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 9a8b1f0 and 0c0ab92.

📒 Files selected for processing (2)
  • UnityMcpBridge/Editor/Tools/ManageScript.cs (8 hunks)
  • UnityMcpBridge/UnityMcpServer~/src/tools/manage_script_edits.py (9 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • UnityMcpBridge/Editor/Tools/ManageScript.cs
🧰 Additional context used
🧬 Code graph analysis (1)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_script_edits.py (1)
UnityMcpBridge/UnityMcpServer~/src/unity_connection.py (1)
  • send_command_with_retry (388-407)
🪛 Ruff (0.12.2)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_script_edits.py

105-106: try-except-pass detected, consider logging the exception

(S110)


105-105: Do not catch blind exception: Exception

(BLE001)


107-107: Comment contains ambiguous (NON-BREAKING HYPHEN). Did you mean - (HYPHEN-MINUS)?

(RUF003)


108-109: try-except-pass detected, consider logging the exception

(S110)


108-108: Do not catch blind exception: Exception

(BLE001)


112-113: try-except-pass detected, consider logging the exception

(S110)


112-112: Do not catch blind exception: Exception

(BLE001)


522-523: try-except-pass detected, consider logging the exception

(S110)


522-522: Do not catch blind exception: Exception

(BLE001)


648-649: try-except-pass detected, consider logging the exception

(S110)


648-648: Do not catch blind exception: Exception

(BLE001)


650-650: Do not catch blind exception: Exception

(BLE001)


671-672: try-except-pass detected, consider logging the exception

(S110)


671-671: Do not catch blind exception: Exception

(BLE001)


798-799: try-except-pass detected, consider logging the exception

(S110)


798-798: Do not catch blind exception: Exception

(BLE001)


884-885: try-except-pass detected, consider logging the exception

(S110)


884-884: Do not catch blind exception: Exception

(BLE001)

🔇 Additional comments (5)
UnityMcpBridge/UnityMcpServer~/src/tools/manage_script_edits.py (5)

505-507: Default to immediate refresh for structured ops — aligned with PR intent.

This matches the “immediate import + compile” path to avoid missed reloads when the Editor is unfocused. LGTM.


639-640: Text-edits defaults look right for batching.

Defaulting to refresh="debounced" and enforcing atomic when multiple spans exist aligns with “one reload per batch.” LGTM.


656-657: Mixed path: confirm debounced on the structured second-leg won’t cause a second reload.

With text-first set to debounced and structured set to debounced, ensure ManageScript.cs coalesces both into the same import/compile cycle when Unity is unfocused. If not, consider leaving the second leg immediate to avoid an extra cycle.


788-791: Good: debounced defaults for server-side apply_text_edits.

Matches the new editor-owned batching model.


851-852: Consistent debounced default on whole-file write.

Keeps text-path behavior uniform.

@dsarno dsarno merged commit 0c52c1c into CoplayDev:main Sep 2, 2025
1 check was pending
@dsarno dsarno deleted the auto-reload branch September 5, 2025 22:44
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