Skip to content

Conversation

@dsarno
Copy link
Owner

@dsarno dsarno commented Dec 28, 2025

What changed

This PR hardens MCP-for-Unity against large-response crashes/timeouts by making “paged + summary-first” the default pattern and by guiding (and correcting) common LLM usage.

  • manage_scene(get_hierarchy): default page_size lowered to 50 (items per page), keeping payloads bounded when callers omit paging params.
  • Server handshake instructions: added explicit payload sizing & paging guidance for:
    • manage_scene(get_hierarchy)
    • manage_gameobject(get_components) (metadata-first; small pages for include_properties=true)
    • manage_asset(search) (page results; avoid previews)
  • manage_asset(search) tool schema: updated parameter docs to steer models toward search_pattern + paging and away from thumbnails.
  • Guardrail for common LLM mistake: server now normalizes manage_asset(search) calls like path="t:MonoScript" into a safer server-side filtered search (search_pattern="t:MonoScript", path="Assets"), preventing “invalid folder ⇒ search entire project” behavior.
  • Test stability: CodexConfigHelperTests now force DevModeForceServerRefresh=false in setup to avoid arg-order failures when dev mode is enabled in the editor.

Why

We’ve seen Unity freeze/crash risk from single-shot, high-payload responses (deep hierarchies, component properties), and token blowups from broad asset searches. The goal is to reduce peak single-response size and make safe behavior the default for naive agents.

A/B validation (same MCPStressTest scene)

Using the same scene asset (Assets/Scenes/MCPStressTest.unity/MCPStressTest.unity in UnityMCPTests), we captured raw JSON responses and measured bytes (wc -c):

A (old, single-shot)

  • get_hierarchy: 30,715 bytes
  • get_components(Heavy_00): 14,139 bytes
  • Peak A: 30,715 bytes

B (new, paged + summary-first)

  • get_hierarchy(page_size=50): 3,825 bytes
  • children paging spot-check (StressRoot_00, page_size=10): 2,530 bytes
  • get_components metadata-only pages (page_size=10): 767 / 769 / 596 bytes
  • get_components properties-on-demand pages (include_properties=true, page_size=3): 1,876 / 1,998 bytes
  • Peak B (captured): 3,825 bytes

Result: peak single-response size dropped from 30,715 → 3,825 bytes (~8× smaller), with include_properties=true staying bounded under ~2KB per page in the test.

Testing

  • Unity EditMode tests: fixed 4 failing CodexConfigHelperTests cases; rerun via MCP: 4/4 passed.
  • Server sanity: python3 -m compileall ok; server unit tests (non-integration) passed (83 passed, 2 skipped, 7 xpassed).

Notes / Follow-ups

  • manage_asset(search) is now more forgiving for LLMs, but callers should still prefer:
    • path="Assets", search_pattern="t:MonoScript", page_size=25, page_number=1, generate_preview=false
  • Untracked stress assets used during local testing were intentionally not committed.

Summary by CodeRabbit

  • New Features

    • Dev Mode toggle to force fresh server installs (--no-cache --refresh) surfaced in settings and generated startup/config snippets.
    • Pagination for scene hierarchy and component listings (page size, cursors, safety limits, optional property inclusion).
    • Server diagnostics added to debug output (version, working directory, argv).
    • New manage_scriptable_object tool listed in the Tools README.
  • Documentation

    • Added payload sizing & paging guidance recommending summary-first, paged reads.
  • Utilities

    • Added shared integer coercion used across paging parameters.

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

* Fix test teardown to avoid dropping MCP bridge

CodexConfigHelperTests was calling MCPServiceLocator.Reset() in TearDown, which disposes the active bridge/transport during MCP-driven test runs. Replace with restoring only the mutated service (IPlatformService).

* Avoid leaking PlatformService in CodexConfigHelperTests

Capture the original IPlatformService before this fixture runs and restore it in TearDown. This preserves the MCP connection safety fix (no MCPServiceLocator.Reset()) while avoiding global state leakage to subsequent tests.
@coderabbitai
Copy link

coderabbitai bot commented Dec 28, 2025

Walkthrough

This PR removes the ScriptableObject design doc, adds paging/safety to scene and gameobject queries, injects a Dev Mode force-refresh flag into CLI/stdio/uvx commands and UI, improves multi-PID server discovery and graceful termination, exposes server diagnostics, adds coerce_int and input normalization, and includes related tests, docs, and dependency pinning.

Changes

Cohort / File(s) Summary
Paging & Safety — manage_gameobject
MCPForUnity/Editor/Tools/ManageGameObject.cs, Server/src/services/tools/manage_gameobject.py, TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageGameObjectTests.cs, Server/tests/integration/test_manage_gameobject_param_coercion.py
Add pageSize/cursor/maxComponents and includeProperties; implement paged retrieval, per-component serialization safety/partial markers, BuildComponentMetadata; server-side param coercion and tests.
Paging & Safety — manage_scene
MCPForUnity/Editor/Tools/ManageScene.cs, Server/src/services/tools/manage_scene.py, TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageSceneHierarchyPagingTests.cs, Server/tests/integration/test_manage_scene_paging_params.py
Add parent/pageSize/cursor/maxNodes/maxDepth/maxChildrenPerNode/includeTransform; ResolveGameObject, BuildGameObjectSummary, GetGameObjectPath, paged hierarchy retrieval and shaping; tests.
Dev Mode Force Refresh (UI & prefs)
MCPForUnity/Editor/Constants/EditorPrefKeys.cs, MCPForUnity/Editor/Windows/Components/Settings/McpSettingsSection.cs, MCPForUnity/Editor/Windows/Components/Settings/McpSettingsSection.uxml
New EditorPrefs key and UI toggle to persist DevModeForceServerRefresh; UI wiring and persistence.
Dev Mode Force Refresh (arg builders)
MCPForUnity/Editor/Helpers/CodexConfigHelper.cs, MCPForUnity/Editor/Helpers/ConfigJsonBuilder.cs, MCPForUnity/Editor/Clients/McpClientConfiguratorBase.cs
Read guarded EditorPrefs flag and inject --no-cache --refresh devFlags into stdio/uvx/CLI argument lists and generated snippets; BuildUvxArgs signature updated to accept dev flag.
Process Management — ServerManagementService
MCPForUnity/Editor/Services/ServerManagementService.cs
Replace single-PID lookup with list-based GetListeningProcessIdsForPort, add GetCurrentProcessIdSafe, LooksLikeMcpServerProcess, TerminateProcess (graceful→forceful), skip Unity Editor PID(s), and inject devFlags into startup commands.
Server Diagnostics & Menu docs
Server/src/services/tools/debug_request_context.py, Server/src/main.py, docs/README-DEV.md
Expose server.version/cwd/argv in debug context; add startup typing guard to builtins; append payload sizing & paging guidance and Dev Mode note to server menu/docs.
Utilities — coercion & reuse
Server/src/services/tools/utils.py, Server/src/services/tools/read_console.py, Server/src/services/tools/run_tests.py
Add coerce_int utility and replace local _coerce_int/_coerce_bool uses with shared coerce_int/coerce_bool to normalize numeric/boolean inputs.
Manage asset improvements
Server/src/services/tools/manage_asset.py
Normalize common search mistakes (move t: queries into search_pattern and scope to Assets), map asset_typefilter_type, expand help text and paging guidance.
Tests & test infra updates
Server/tests/integration/test_debug_request_context_diagnostics.py, Server/tests/integration/test_tool_signatures_paging.py, Server/tests/integration/test_manage_gameobject_param_coercion.py, Server/tests/integration/test_manage_scene_paging_params.py, TestProjects/UnityMCPTests/Assets/Tests/EditMode/Helpers/CodexConfigHelperTests.cs, TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageGameObjectTests.cs, TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageSceneHierarchyPagingTests.cs, TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageScriptableObjectTests.cs
New and updated unit/integration tests for diagnostics, paging param signatures and coercion, ManageGameObject/ManageScene paging behavior, and test prefs setup/teardown.
Deps pinning
Server/pyproject.toml
Pin fastmcp to ==2.14.1.
Removed design doc
FixscriptableobjecPlan.md
Deleted the ScriptableObject creation/patching design and tooling plan document.

Sequence Diagram(s)

sequenceDiagram
  participant Editor as Unity Editor (UI)
  participant Settings as Settings UI
  participant Config as MCP Client Configurator
  participant UVX as UVX / uvx CLI (stdio/CLI)
  participant Server as Local MCP HTTP Server
  participant ProcMgr as ServerManagementService

  rect rgb(240,248,255)
  Editor->>Settings: toggle Dev Mode
  Settings->>Editor: persist EditorPrefs.DevModeForceServerRefresh
  Settings->>Config: update configurator outputs/snippets (include devFlags)
  end

  rect rgb(245,245,220)
  Editor->>UVX: StartLocalHttpServer request (includes devFlags if enabled)
  UVX->>Server: launch command with args (--no-cache --refresh ...)? 
  Server-->>UVX: started / listening PIDs
  end

  rect rgb(240,255,240)
  Editor->>ProcMgr: StopLocalHttpServer
  ProcMgr->>ProcMgr: GetListeningProcessIdsForPort
  ProcMgr->>ProcMgr: filter out Editor PID via GetCurrentProcessIdSafe & LooksLikeMcpServerProcess
  ProcMgr->>ProcMgr: TerminateProcess(pid) (graceful → force)
  ProcMgr-->>Editor: stop result per-PID
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

codex

Poem

🐰 I hop through paged fields, tidy and spry,

Fresh flags unfurled to fetch a brand-new try,
Servers whisper version, cwd, and argv,
Pages roll steady, and processes end kind —
A rabbit's small cheer for clean, careful mind.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 13.43% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: payload-safe paging for hierarchy/components, safer asset search, and documentation updates align with the substantial changes across the codebase.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch improve-get-hierarchy-and-get-components

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

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (1)
MCPForUnity/Editor/Helpers/ConfigJsonBuilder.cs (1)

155-173: Argument order for --no-cache --refresh is inconsistent across implementations.

ConfigJsonBuilder places --no-cache --refresh after --from <url>, while McpClientConfiguratorBase.cs (line 430) and ServerManagementService.cs (line 463) both place them before --from. Although most CLI tools are order-agnostic, this inconsistency should be harmonized—either reorder the args in ConfigJsonBuilder to match the other two files, or verify that uvx accepts arguments in this order.

🧹 Nitpick comments (12)
Server/src/services/tools/manage_asset.py (1)

118-120: Consider narrowing the exception catch.

The broad except Exception on line 119 is overly defensive. Since path has a type annotation of str, the most likely failure mode is receiving an unexpected type. Consider catching only AttributeError or TypeError to allow unexpected errors to propagate while still handling the intended edge case.

🔎 Proposed refinement
-        try:
-            raw_path = (path or "").strip()
-        except Exception:
-            raw_path = ""
+        try:
+            raw_path = (path or "").strip()
+        except (AttributeError, TypeError):
+            # Handle case where path is not a string despite type annotation
+            raw_path = ""

Based on static analysis hints (Ruff BLE001).

Server/src/main.py (1)

278-281: Replace en-dashes with hyphens for consistency.

The strings use en-dash characters (, U+2013) instead of hyphen-minus (-, U+002D) in ranges like 10–25. While visually similar, this could cause issues if the text is parsed or copied. As flagged by Ruff (RUF001).

🔎 Proposed fix
-  - Start with `include_properties=false` (metadata-only) and small `page_size` (e.g. **10–25**).
-  - Only request `include_properties=true` when needed; keep `page_size` small (e.g. **3–10**) to bound payloads.
+  - Start with `include_properties=false` (metadata-only) and small `page_size` (e.g. **10-25**).
+  - Only request `include_properties=true` when needed; keep `page_size` small (e.g. **3-10**) to bound payloads.
   - `manage_asset(action="search")`:
-  - Use paging (`page_size`, `page_number`) and keep `page_size` modest (e.g. **25–50**) to avoid token-heavy responses.
+  - Use paging (`page_size`, `page_number`) and keep `page_size` modest (e.g. **25-50**) to avoid token-heavy responses.
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageGameObjectTests.cs (1)

108-145: Consider asserting exact item count for consistency.

Line 136 uses Assert.IsTrue(items.Count > 0) while the first test asserts Assert.AreEqual(2, items.Count). For consistency and stronger coverage, consider asserting the exact expected count here as well (the GameObject has Transform + Rigidbody + BoxCollider = 3 components, so with pageSize=2 you'd expect exactly 2).

🔎 Proposed fix
             var items = data["items"] as JArray;
             Assert.IsNotNull(items);
-            Assert.IsTrue(items.Count > 0);
+            Assert.AreEqual(2, items.Count, "Expected exactly pageSize items.");
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageSceneHierarchyPagingTests.cs (1)

106-107: Minor: Remove trailing blank lines.

Extra blank lines at end of file.

MCPForUnity/Editor/Tools/ManageScene.cs (1)

605-619: Potential performance concern with name-based resolution.

When resolving by name, this iterates through all root objects and their entire child hierarchies via GetComponentsInChildren<Transform>. For scenes with deep hierarchies, this could be slow.

Consider adding a fast-path exit once a match is found, or document that ID-based resolution is preferred for performance-critical paths.

🔎 Suggested optimization

The current implementation already returns on first match, which is good. However, GetComponentsInChildren allocates an array of all transforms before iterating. For very large hierarchies, consider using a recursive search that can exit early:

// Alternative: recursive early-exit search
GameObject FindByNameRecursive(Transform parent, string targetName)
{
    foreach (Transform child in parent)
    {
        if (child.gameObject.name == targetName) return child.gameObject;
        var found = FindByNameRecursive(child, targetName);
        if (found != null) return found;
    }
    return null;
}
Server/src/services/tools/manage_gameobject.py (1)

101-114: Duplicated _coerce_int helper across multiple files.

This exact helper function is now duplicated in manage_asset.py, read_console.py, manage_scene.py, and here. Consider extracting it to services/tools/utils.py alongside the existing coerce_bool function to follow DRY principles.

The broad Exception catch (flagged by Ruff BLE001) is acceptable here given the defensive coercion intent—it ensures any unexpected input gracefully falls back to the default.

Proposed refactor to utils.py
# In Server/src/services/tools/utils.py, add:
def coerce_int(value: Any, default: int | None = None) -> int | None:
    """Attempt to coerce a loosely-typed value to an integer."""
    if value is None:
        return default
    try:
        if isinstance(value, bool):
            return default
        if isinstance(value, int):
            return int(value)
        s = str(value).strip()
        if s.lower() in ("", "none", "null"):
            return default
        return int(float(s))
    except Exception:
        return default
Server/tests/integration/test_manage_gameobject_param_coercion.py (1)

69-72: Consider tightening assertions to verify coercion.

The assertions accept both coerced (25) and original ("25") values, which is flexible but doesn't verify that coercion actually happened. Based on the implementation in manage_gameobject.py, numeric values should be coerced to integers (e.g., page_size should be 25, not "25").

If coercion is expected, the assertions could be stricter:

Stricter assertions (if coercion is verified)
-    assert p["pageSize"] in (25, "25")
-    assert p["cursor"] in (50, "50")
-    assert p["maxComponents"] in (100, "100")
-    assert p["includeProperties"] in (True, "true")
+    assert p["pageSize"] == 25
+    assert p["cursor"] == 50
+    assert p["maxComponents"] == 100
+    assert p["includeProperties"] is True
MCPForUnity/Editor/Helpers/CodexConfigHelper.cs (2)

42-52: Duplicated dev-mode flag logic and empty catch blocks.

The devForceRefresh reading and args injection pattern is duplicated between BuildCodexServerBlock and CreateUnityMcpTable. Additionally, the empty catch block silently swallows any EditorPrefs access issues.

Consider extracting the flag reading to a helper method:

Proposed helper extraction
private static bool GetDevModeForceRefresh()
{
    try
    {
        return EditorPrefs.GetBool(EditorPrefKeys.DevModeForceServerRefresh, false);
    }
    catch
    {
        return false;
    }
}

private static void AddDevModeArgs(TomlArray args, bool devForceRefresh)
{
    if (devForceRefresh)
    {
        args.Add(new TomlString { Value = "--no-cache" });
        args.Add(new TomlString { Value = "--refresh" });
    }
}

197-207: Same duplication as noted above.

This block mirrors the logic in BuildCodexServerBlock (lines 42-52). The suggested helper extraction would consolidate both occurrences.

MCPForUnity/Editor/Clients/McpClientConfiguratorBase.cs (1)

544-548: Consider extracting the conditional to improve readability.

The inline conditional in the interpolated string works correctly but is dense. Consider extracting to a local variable for clarity, similar to how it's done in Register().

🔎 Optional refactor for consistency
 string gitUrl = AssetPathUtility.GetMcpServerGitUrl();
 bool devForceRefresh = false;
 try { devForceRefresh = EditorPrefs.GetBool(EditorPrefKeys.DevModeForceServerRefresh, false); } catch { }
+string devFlags = devForceRefresh ? "--no-cache --refresh " : string.Empty;

 return "# Register the MCP server with Claude Code:\n" +
-       $"claude mcp add --transport stdio UnityMCP -- \"{uvxPath}\" {(devForceRefresh ? "--no-cache --refresh " : string.Empty)}--from \"{gitUrl}\" mcp-for-unity\n\n" +
+       $"claude mcp add --transport stdio UnityMCP -- \"{uvxPath}\" {devFlags}--from \"{gitUrl}\" mcp-for-unity\n\n" +
Server/src/services/tools/manage_scene.py (1)

44-57: Consider extracting coercion helpers to reduce duplication.

The _coerce_int function is duplicated between manage_scene.py and manage_gameobject.py. Consider extracting to a shared utility module to improve maintainability.

# In a new file like services/tools/coercion.py:
def coerce_int(value, default=None):
    """Coerce a value to int, handling string/bool edge cases."""
    if value is None:
        return default
    try:
        if isinstance(value, bool):
            return default
        if isinstance(value, int):
            return int(value)
        s = str(value).strip()
        if s.lower() in ("", "none", "null"):
            return default
        return int(float(s))
    except Exception:
        return default
MCPForUnity/Editor/Services/ServerManagementService.cs (1)

357-358: Minor: Windows process name check could miss edge cases.

The check combined.Contains("uv.exe") after combined.Contains("uvx") is partially redundant since "uvx" would match both "uvx" and "uvx.exe". However, explicitly checking for "uv.exe" is correct since "uv" alone might match unrelated strings.

🔎 Consider tightening the Windows check
-                        return combined.Contains("python") || combined.Contains("uvx") || combined.Contains("uv.exe") || combined.Contains("uvx.exe");
+                        // Check for common MCP server process names
+                        return combined.Contains("python.exe") || combined.Contains("python ") ||
+                               combined.Contains("uvx.exe") || combined.Contains("uv.exe");
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f70c703 and 4045d5a.

⛔ Files ignored due to path filters (1)
  • Server/uv.lock is excluded by !**/*.lock
📒 Files selected for processing (25)
  • FixscriptableobjecPlan.md
  • MCPForUnity/Editor/Clients/McpClientConfiguratorBase.cs
  • MCPForUnity/Editor/Constants/EditorPrefKeys.cs
  • MCPForUnity/Editor/Helpers/CodexConfigHelper.cs
  • MCPForUnity/Editor/Helpers/ConfigJsonBuilder.cs
  • MCPForUnity/Editor/Services/ServerManagementService.cs
  • MCPForUnity/Editor/Tools/ManageGameObject.cs
  • MCPForUnity/Editor/Tools/ManageScene.cs
  • MCPForUnity/Editor/Windows/Components/Settings/McpSettingsSection.cs
  • MCPForUnity/Editor/Windows/Components/Settings/McpSettingsSection.uxml
  • Server/pyproject.toml
  • Server/src/main.py
  • Server/src/services/tools/debug_request_context.py
  • Server/src/services/tools/manage_asset.py
  • Server/src/services/tools/manage_gameobject.py
  • Server/src/services/tools/manage_scene.py
  • Server/tests/integration/test_debug_request_context_diagnostics.py
  • Server/tests/integration/test_manage_gameobject_param_coercion.py
  • Server/tests/integration/test_manage_scene_paging_params.py
  • Server/tests/integration/test_tool_signatures_paging.py
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Helpers/CodexConfigHelperTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageGameObjectTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageSceneHierarchyPagingTests.cs
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageSceneHierarchyPagingTests.cs.meta
  • docs/README-DEV.md
💤 Files with no reviewable changes (1)
  • FixscriptableobjecPlan.md
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-10-21T05:19:59.258Z
Learnt from: dsarno
Repo: dsarno/unity-mcp PR: 85
File: tests/test_telemetry_queue_worker.py:33-37
Timestamp: 2025-10-21T05:19:59.258Z
Learning: In the unity-mcp repository, pyproject.toml is located in MCPForUnity/UnityMcpServer~/src/, not at the repository root. When testing code that imports telemetry.py, the working directory should be changed to SRC (MCPForUnity/UnityMcpServer~/src/) so that telemetry.py's get_package_version() can find pyproject.toml via relative path.

Applied to files:

  • Server/src/services/tools/debug_request_context.py
🧬 Code graph analysis (17)
Server/tests/integration/test_manage_gameobject_param_coercion.py (3)
Server/tests/integration/test_manage_scene_paging_params.py (1)
  • fake_send (11-13)
Server/src/services/tools/manage_gameobject.py (1)
  • manage_gameobject (16-268)
Server/tests/integration/test_helpers.py (1)
  • DummyContext (16-55)
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageGameObjectTests.cs (1)
MCPForUnity/Editor/Tools/ManageGameObject.cs (1)
  • HandleCommand (42-253)
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Helpers/CodexConfigHelperTests.cs (1)
MCPForUnity/Editor/Constants/EditorPrefKeys.cs (1)
  • EditorPrefKeys (7-49)
MCPForUnity/Editor/Tools/ManageScene.cs (1)
MCPForUnity/Editor/Tools/ManageGameObject.cs (6)
  • List (1632-1791)
  • List (2639-2676)
  • List (2699-2717)
  • List (2722-2761)
  • List (2769-2804)
  • GameObject (1604-1627)
MCPForUnity/Editor/Windows/Components/Settings/McpSettingsSection.cs (1)
MCPForUnity/Editor/Constants/EditorPrefKeys.cs (1)
  • EditorPrefKeys (7-49)
MCPForUnity/Editor/Clients/McpClientConfiguratorBase.cs (2)
MCPForUnity/Editor/Constants/EditorPrefKeys.cs (1)
  • EditorPrefKeys (7-49)
MCPForUnity/Editor/Helpers/AssetPathUtility.cs (1)
  • uvxPath (169-176)
Server/tests/integration/test_debug_request_context_diagnostics.py (1)
Server/src/services/tools/debug_request_context.py (1)
  • debug_request_context (16-80)
Server/src/services/tools/manage_scene.py (1)
Server/src/services/tools/manage_gameobject.py (1)
  • _coerce_int (101-114)
Server/src/services/tools/manage_gameobject.py (4)
Server/src/services/tools/manage_asset.py (1)
  • _coerce_int (93-106)
Server/src/services/tools/manage_scene.py (1)
  • _coerce_int (44-57)
Server/src/services/tools/read_console.py (1)
  • _coerce_int (61-74)
Server/src/services/tools/utils.py (1)
  • coerce_bool (11-24)
Server/tests/integration/test_manage_scene_paging_params.py (2)
Server/tests/integration/test_helpers.py (1)
  • DummyContext (16-55)
Server/src/services/tools/manage_scene.py (1)
  • manage_scene (13-122)
Server/src/services/tools/manage_asset.py (1)
Server/tests/integration/test_helpers.py (1)
  • info (36-37)
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageSceneHierarchyPagingTests.cs (1)
MCPForUnity/Editor/Tools/ManageScene.cs (1)
  • HandleCommand (90-191)
Server/src/services/tools/debug_request_context.py (1)
Server/src/core/telemetry.py (1)
  • get_package_version (66-80)
Server/tests/integration/test_tool_signatures_paging.py (2)
Server/src/services/tools/manage_scene.py (1)
  • manage_scene (13-122)
Server/src/services/tools/manage_gameobject.py (1)
  • manage_gameobject (16-268)
MCPForUnity/Editor/Helpers/CodexConfigHelper.cs (3)
MCPForUnity/Editor/Constants/EditorPrefKeys.cs (1)
  • EditorPrefKeys (7-49)
MCPForUnity/Editor/Helpers/AssetPathUtility.cs (1)
  • uvxPath (169-176)
MCPForUnity/Editor/External/Tommy.cs (1)
  • TomlString (168-193)
MCPForUnity/Editor/Services/ServerManagementService.cs (3)
MCPForUnity/Editor/Helpers/McpLog.cs (4)
  • McpLog (7-52)
  • Info (37-41)
  • Warn (43-46)
  • Error (48-51)
MCPForUnity/Editor/Helpers/ExecPath.cs (2)
  • ExecPath (12-272)
  • TryRun (161-227)
MCPForUnity/Editor/Constants/EditorPrefKeys.cs (1)
  • EditorPrefKeys (7-49)
MCPForUnity/Editor/Helpers/ConfigJsonBuilder.cs (1)
MCPForUnity/Editor/Constants/EditorPrefKeys.cs (1)
  • EditorPrefKeys (7-49)
🪛 LanguageTool
docs/README-DEV.md

[style] ~174-~174: As an alternative to the over-used intensifier ‘very’, consider replacing this phrase.
Context: ...ded) Some Unity tool calls can return very large JSON payloads (deep hierarchies, compo...

(EN_WEAK_ADJECTIVE)

🪛 Ruff (0.14.10)
Server/tests/integration/test_manage_gameobject_param_coercion.py

43-43: Unused function argument: cmd

(ARG001)


43-43: Unused function argument: kwargs

(ARG001)

Server/src/main.py

34-35: try-except-pass detected, consider logging the exception

(S110)


34-34: Do not catch blind exception: Exception

(BLE001)


278-278: String contains ambiguous (EN DASH). Did you mean - (HYPHEN-MINUS)?

(RUF001)


279-279: String contains ambiguous (EN DASH). Did you mean - (HYPHEN-MINUS)?

(RUF001)


281-281: String contains ambiguous (EN DASH). Did you mean - (HYPHEN-MINUS)?

(RUF001)

Server/src/services/tools/manage_scene.py

72-72: Do not catch blind exception: Exception

(BLE001)

Server/src/services/tools/manage_gameobject.py

113-113: Do not catch blind exception: Exception

(BLE001)

Server/tests/integration/test_manage_scene_paging_params.py

11-11: Unused function argument: cmd

(ARG001)


11-11: Unused function argument: kwargs

(ARG001)

Server/src/services/tools/manage_asset.py

119-119: Do not catch blind exception: Exception

(BLE001)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Greptile Review
🔇 Additional comments (35)
Server/src/services/tools/manage_asset.py (2)

18-46: LGTM: Excellent documentation improvements for LLM guidance.

The updated parameter descriptions and tool-level tips effectively steer models toward payload-safe patterns (paging, avoiding previews, using search_pattern). This aligns well with the PR's objective of preventing large-response crashes.


111-133: Smart normalization logic for LLM-generated inputs.

This server-side guardrail effectively prevents common mistakes where models place Unity AssetDatabase queries (like t:MonoScript) into the path parameter instead of search_pattern, which would trigger inefficient project-wide searches. The mapping of asset_type to filter_type is also a sensible fallback. Logging at lines 126 and 132 provides good visibility for debugging.

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

1-11: LGTM!

Standard Unity-generated meta file for the new paging test suite.

TestProjects/UnityMCPTests/Assets/Tests/EditMode/Helpers/CodexConfigHelperTests.cs (1)

35-36: LGTM!

The test isolation for DevModeForceServerRefresh follows the established pattern used for GitUrlOverride and UseHttpTransport. Correctly tracks both presence and value, ensures deterministic uvx args ordering during tests, and properly restores the original state in teardown.

Also applies to: 46-47, 58-60, 98-106

docs/README-DEV.md (2)

76-76: Good addition documenting the dev-mode behavior.

Clear explanation of the --no-cache --refresh flags and when to use them.


172-206: Well-documented paging defaults.

The "Payload sizing & paging defaults" section provides actionable guidance for callers. The parameter descriptions with default values and clamping ranges align with the implementation in ManageScene.cs.

Server/src/main.py (1)

11-35: Reasonable startup safety guard.

This workaround handles edge cases where tool signature evaluation via eval() lacks common typing names in globals. The broad exception catch with pass is intentional here to avoid breaking startup—acceptable given the # pragma: no cover marker and defensive nature.

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

72-106: Good test coverage for default paging behavior.

The test properly validates the paging contract: includeProperties=false by default, exactly pageSize items returned, and lightweight metadata without the properties field.

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

29-103: Comprehensive paging test covering key scenarios.

Good coverage of:

  1. Root pagination with truncation detection
  2. Cursor-based continuation to fetch subsequent pages
  3. Children pagination via parent instance ID

The test validates the paging envelope structure (scope, truncated, next_cursor, items.Count).

MCPForUnity/Editor/Tools/ManageScene.cs (3)

29-37: Well-structured paging parameters.

The SceneCommand extension with paging fields and the corresponding parsing in ToSceneCommand follow the established patterns. Good support for both camelCase and snake_case parameter names.

Also applies to: 74-84


502-564: Solid paging implementation with sensible defaults.

The defaults are well-chosen for bounding response sizes:

  • pageSize=50 (clamped 1-500)
  • maxNodes=1000 (clamped 1-5000)

The response envelope with scope, truncated, next_cursor, and total provides clear paging metadata for callers.


624-660: Good summary structure for paged responses.

The BuildGameObjectSummary method returns useful metadata including childrenCursor and childrenPageSizeDefault to guide callers on fetching children. Setting childrenTruncated=true when childCount > 0 is a reasonable signal that children exist but aren't inlined.

Server/pyproject.toml (1)

33-33: [Your rewritten review comment text here]
[Exactly ONE classification tag]

Server/src/services/tools/manage_gameobject.py (1)

221-224: LGTM!

The paging parameters are correctly assembled and will be filtered by the {k: v for k, v in params.items() if v is not None} comprehension at line 234, ensuring only valid values are sent to Unity.

Server/tests/integration/test_manage_gameobject_param_coercion.py (1)

43-45: LGTM!

The mock function correctly captures the params for assertion. The unused cmd and kwargs arguments (flagged by Ruff ARG001) are expected for a mock that must match the signature of async_send_command_with_retry.

MCPForUnity/Editor/Windows/Components/Settings/McpSettingsSection.uxml (1)

45-50: LGTM!

The Dev Mode UI block is well-structured with clear help text explaining the trade-off (slower startup but avoids stale cached builds). The toggle name dev-mode-force-refresh-toggle aligns with the C# binding in McpSettingsSection.cs.

Server/src/services/tools/debug_request_context.py (1)

57-61: LGTM!

The server metadata block provides useful diagnostics for debugging. Using list(sys.argv) creates a copy to prevent mutation, and get_package_version() has proper fallback handling. Since this is a debug endpoint, exposing runtime context is appropriate.

MCPForUnity/Editor/Constants/EditorPrefKeys.cs (1)

23-23: LGTM!

The new DevModeForceServerRefresh constant follows the established naming convention and is logically placed with related override settings.

Server/tests/integration/test_manage_scene_paging_params.py (1)

37-42: Same flexible assertion pattern as gameobject test.

The assertions accept both coerced and original values. For manage_scene.py, the coercion uses default=None, so failed coercions would exclude the param entirely. The assertions verify the params are present with expected values, which is sufficient for integration testing.

MCPForUnity/Editor/Windows/Components/Settings/McpSettingsSection.cs (2)

157-161: LGTM!

The callback correctly persists the toggle value to EditorPrefs and invokes OnHttpServerCommandUpdateRequested to trigger config regeneration. This follows the same pattern used for other settings like gitUrlOverride.


110-110: LGTM!

The toggle is correctly initialized from EditorPrefs with a safe default of false, ensuring dev mode is opt-in.

Server/tests/integration/test_tool_signatures_paging.py (1)

1-32: LGTM - Signature verification tests for paging parameters.

These tests ensure the paging/safety parameters are present in the function signatures, providing regression protection if someone accidentally removes them. The tests are appropriately minimal for their purpose.

Server/tests/integration/test_debug_request_context_diagnostics.py (1)

4-26: LGTM - Test verifies server diagnostics are exposed.

The test correctly validates that debug_request_context returns server metadata (version, cwd, argv). Using monkeypatch to stabilize the version value is a good practice for deterministic assertions.

MCPForUnity/Editor/Clients/McpClientConfiguratorBase.cs (1)

426-430: LGTM - Dev mode refresh flags correctly integrated.

The implementation properly reads the DevModeForceServerRefresh preference with a safe try/catch fallback and injects the --no-cache --refresh flags into the stdio transport command when enabled.

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

84-87: LGTM - EditorPrefs access with safe fallback.

The try/catch pattern consistently guards against potential EditorPrefs exceptions with a sensible default of false.

MCPForUnity/Editor/Tools/ManageGameObject.cs (4)

183-212: LGTM - Robust coercion helpers for paging parameters.

The CoerceInt and CoerceBool local functions handle various input formats gracefully (null, boolean, empty string, "none", "null", numeric strings). This defensive parsing accommodates different client behaviors.


1250-1268: LGTM - Well-bounded paging with safety limits.

The clamping of pageSize (1-200), cursor (≥0), and maxComponents (1-500) provides good guardrails. Building a stable component list upfront ensures consistent pagination across requests.


1273-1315: LGTM - Payload budget prevents response explosion.

The 250KB payload budget (maxPayloadChars) with early termination is a sensible safeguard when includeProperties=true. The per-component error handling ensures one serialization failure doesn't break the entire response.


1344-1357: LGTM - Compact metadata helper.

BuildComponentMetadata returns essential information (typeName, instanceID, enabled) without deep serialization, supporting the metadata-first pattern when includeProperties=false.

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

59-74: LGTM - Comprehensive boolean coercion.

The _coerce_bool function handles a wide range of truthy/falsy string representations ("true", "1", "yes", "y", "on" and their opposites), which accommodates various client input formats.

Regarding the static analysis hint about catching Exception on line 72: this is acceptable here as the function is purely defensive and must never propagate errors—returning default is the correct fallback behavior.


96-111: LGTM - Conditional param inclusion.

Paging parameters are only added to the Unity command payload when they have non-None values after coercion, avoiding unnecessary parameter bloat.

MCPForUnity/Editor/Services/ServerManagementService.cs (4)

233-273: LGTM - Robust multi-PID termination with safety guards.

The implementation correctly:

  1. Discovers all processes listening on the port
  2. Refuses to terminate the Unity Editor process
  3. Refuses to terminate processes that don't look like MCP servers
  4. Reports per-PID outcomes

This prevents accidental termination of unrelated services.


345-394: Well-designed process identification heuristics.

LooksLikeMcpServerProcess uses reasonable heuristics:

  • Explicitly excludes Unity/Unity Hub processes
  • Requires positive indicators (uv/uvx/python) AND MCP-related markers (mcp-for-unity, --transport http)

The empty catch on line 391 is acceptable for this defensive helper that must never throw.


396-427: LGTM - Graceful-then-forceful termination strategy.

Trying graceful termination first (kill -15 / taskkill) before escalating to forceful (kill -9 / taskkill /F) is the correct approach, allowing the process to clean up resources if possible.


458-464: LGTM - Dev mode flags integrated into server command.

Consistent with other files, the DevModeForceServerRefresh preference is read with a safe try/catch and the --no-cache --refresh flags are injected when enabled.

@greptile-apps
Copy link

greptile-apps bot commented Dec 28, 2025

Greptile Summary

Implemented payload-safe paging defaults and LLM-mistake hardening to prevent Unity freeze/crash risks from large JSON responses.

Key Changes:

  • manage_scene(get_hierarchy): default page_size reduced to 50 (from unbounded), clamped to 500 max; cursor-based paging returns single-level summaries to bound peak response size
  • manage_gameobject(get_components): added paging with page_size, cursor, max_components, and include_properties (defaults to false for metadata-only responses)
  • manage_asset(search): hardened against common LLM mistake of putting t:MonoScript queries in path field; server now normalizes to search_pattern + path="Assets" to prevent unbounded project-wide search
  • Server handshake instructions: added explicit payload sizing & paging guidance for all three tools
  • Tool parameter docs: updated to recommend small page sizes and discourage generate_preview for asset searches
  • Test stability: CodexConfigHelperTests now forces DevModeForceServerRefresh=false in setup to ensure deterministic arg ordering
  • Dev Mode feature: added UI toggle + docs for --no-cache --refresh flag to force fresh server installs during development

Validation:
PR includes A/B measurement showing peak single-response size dropped from 30,715 → 3,825 bytes (~8× reduction) using same test scene. All EditMode tests passing (4/4 fixed tests + new paging test).

Confidence Score: 5/5

  • Safe to merge with high confidence; changes are well-tested, defensive, and address real stability issues
  • Score reflects thorough implementation with defensive defaults, comprehensive test coverage (new paging tests + fixed stability tests), real A/B validation showing 8× payload reduction, and conservative default values (page_size=50) that balance safety with usability. The LLM-mistake normalization in manage_asset is particularly valuable for preventing accidental unbounded searches.
  • No files require special attention

Important Files Changed

Filename Overview
Server/src/services/tools/manage_scene.py Added paging parameters (page_size, cursor, max_nodes, etc.) with safe defaults (page_size=50) to prevent large payload crashes; parameter coercion logic is solid
Server/src/services/tools/manage_gameobject.py Added paging support for get_components action with page_size, cursor, max_components, and include_properties parameters; parameter validation is thorough
Server/src/services/tools/manage_asset.py Added LLM mistake normalization for search: converts misplaced t:MonoScript queries from path to search_pattern; updated docs to guide toward paging and away from previews
Server/src/main.py Added comprehensive payload sizing guidance in server instructions for get_hierarchy, get_components, and asset search operations
MCPForUnity/Editor/Tools/ManageScene.cs Implemented paged hierarchy retrieval with GetSceneHierarchyPaged; default page_size=50, clamped to 500 max; returns summary-first with cursor-based paging
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Helpers/CodexConfigHelperTests.cs Fixed test instability by forcing DevModeForceServerRefresh=false in setup to ensure deterministic arg ordering regardless of editor settings

Sequence Diagram

sequenceDiagram
    participant LLM as LLM/Agent
    participant Server as Python Server
    participant Unity as Unity C# Handler
    
    Note over LLM,Unity: Paged Hierarchy Request (new flow)
    LLM->>Server: manage_scene(action="get_hierarchy", page_size=50)
    Server->>Server: Coerce page_size string/int (default: None → 50)
    Server->>Unity: {"action": "get_hierarchy", "pageSize": 50}
    Unity->>Unity: Clamp pageSize (50, max: 500)
    Unity->>Unity: Fetch roots, slice [0:50]
    Unity-->>Server: {items: [...], cursor: 0, next_cursor: "50", truncated: true, total: 100}
    Server-->>LLM: Success + paged data
    
    Note over LLM,Unity: Follow-up page request
    LLM->>Server: manage_scene(action="get_hierarchy", page_size=50, cursor=50)
    Server->>Unity: {"action": "get_hierarchy", "pageSize": 50, "cursor": 50}
    Unity->>Unity: Slice [50:100]
    Unity-->>Server: {items: [...], cursor: 50, next_cursor: null, truncated: false, total: 100}
    Server-->>LLM: Success + remaining data
    
    Note over LLM,Unity: Asset Search (hardened flow)
    LLM->>Server: manage_asset(action="search", path="t:MonoScript")
    Server->>Server: Detect "t:" prefix in path (LLM mistake)
    Server->>Server: Normalize: search_pattern="t:MonoScript", path="Assets"
    Server->>Unity: {"action": "search", "path": "Assets", "searchPattern": "t:MonoScript"}
    Unity->>Unity: Bounded search in Assets folder
    Unity-->>Server: {results: [...]}
    Server-->>LLM: Success + safe results
    
    Note over LLM,Unity: Component Properties (metadata-first)
    LLM->>Server: manage_gameobject(action="get_components", target="Player", page_size=10)
    Server->>Server: include_properties defaults to None (false)
    Server->>Unity: {"action": "get_components", "target": "Player", "pageSize": 10}
    Unity->>Unity: Return metadata only (no properties)
    Unity-->>Server: {components: [{name, type}, ...], truncated: false}
    Server-->>LLM: Lightweight component list
    
    LLM->>Server: manage_gameobject(action="get_components", target="Player", page_size=3, include_properties=true)
    Server->>Unity: {"action": "get_components", "includeProperties": true, "pageSize": 3}
    Unity->>Unity: Return first 3 components with full properties
    Unity-->>Server: {components: [{name, type, properties: {...}}, ...], next_cursor: "3"}
    Server-->>LLM: Detailed properties (bounded)
Loading

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (4)
Server/src/services/tools/run_tests.py (1)

59-60: Consider removing the redundant wrapper.

The _coerce_int function now simply delegates to coerce_int without adding any logic. You can call coerce_int directly at line 74 and remove this wrapper.

🔎 Proposed simplification
-    # Coerce timeout defensively (string/float -> int)
-    def _coerce_int(value, default=None):
-        return coerce_int(value, default=default)
-
     # Coerce string or list to list of strings
     def _coerce_string_list(value) -> list[str] | None:

Then at line 74:

-    ts = _coerce_int(timeout_seconds)
+    ts = coerce_int(timeout_seconds)
MCPForUnity/Editor/Clients/McpClientConfiguratorBase.cs (1)

426-430: Consider extracting the dev-mode flag reading pattern.

This pattern is duplicated at lines 544-546. Consider extracting a helper method similar to CodexConfigHelper.GetDevModeForceRefresh() to reduce duplication and maintain consistency.

🔎 Proposed helper extraction

Add a private helper method in ClaudeCliMcpConfigurator:

private static bool GetDevModeForceRefresh()
{
    try { return EditorPrefs.GetBool(EditorPrefKeys.DevModeForceServerRefresh, false); }
    catch { return false; }
}

Then replace both occurrences:

-bool devForceRefresh = false;
-try { devForceRefresh = EditorPrefs.GetBool(EditorPrefKeys.DevModeForceServerRefresh, false); } catch { }
+bool devForceRefresh = GetDevModeForceRefresh();
Server/src/services/tools/utils.py (1)

70-71: Redundant int() cast on already-int value.

When value is already an int, calling int(value) is a no-op. You can return value directly.

🔎 Proposed fix
         if isinstance(value, int):
-            return int(value)
+            return value
Server/src/services/tools/read_console.py (1)

42-53: Consider using coerce_bool from utils in a follow-up.

There's a coerce_bool function in utils.py that could replace this local _coerce_bool. This is outside the scope of this PR but would complete the centralization effort.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4045d5a and b18cd93.

📒 Files selected for processing (12)
  • MCPForUnity/Editor/Clients/McpClientConfiguratorBase.cs
  • MCPForUnity/Editor/Helpers/CodexConfigHelper.cs
  • MCPForUnity/Editor/Helpers/ConfigJsonBuilder.cs
  • Server/src/main.py
  • Server/src/services/tools/manage_asset.py
  • Server/src/services/tools/manage_gameobject.py
  • Server/src/services/tools/manage_scene.py
  • Server/src/services/tools/read_console.py
  • Server/src/services/tools/run_tests.py
  • Server/src/services/tools/utils.py
  • Server/tests/integration/test_manage_gameobject_param_coercion.py
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageSceneHierarchyPagingTests.cs
🚧 Files skipped from review as they are similar to previous changes (2)
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageSceneHierarchyPagingTests.cs
  • Server/src/services/tools/manage_gameobject.py
🧰 Additional context used
🧬 Code graph analysis (5)
Server/tests/integration/test_manage_gameobject_param_coercion.py (2)
Server/tests/integration/test_manage_scene_paging_params.py (1)
  • fake_send (11-13)
Server/src/services/tools/manage_gameobject.py (1)
  • manage_gameobject (16-254)
Server/src/services/tools/manage_scene.py (3)
Server/src/services/tools/utils.py (1)
  • coerce_int (63-77)
Server/src/services/tools/run_tests.py (1)
  • _coerce_int (59-60)
Server/src/services/tools/read_console.py (1)
  • _coerce_bool (42-53)
MCPForUnity/Editor/Helpers/CodexConfigHelper.cs (3)
MCPForUnity/Editor/Constants/EditorPrefKeys.cs (1)
  • EditorPrefKeys (7-49)
MCPForUnity/Editor/External/Tommy.cs (1)
  • TomlString (168-193)
MCPForUnity/Editor/Helpers/AssetPathUtility.cs (1)
  • uvxPath (169-176)
Server/src/services/tools/run_tests.py (1)
Server/src/services/tools/utils.py (1)
  • coerce_int (63-77)
Server/src/services/tools/read_console.py (1)
Server/src/services/tools/utils.py (1)
  • coerce_int (63-77)
🪛 Ruff (0.14.10)
Server/tests/integration/test_manage_gameobject_param_coercion.py

43-43: Unused function argument: cmd

(ARG001)


43-43: Unused function argument: kwargs

(ARG001)

Server/src/main.py

34-35: try-except-pass detected, consider logging the exception

(S110)


34-34: Do not catch blind exception: Exception

(BLE001)

Server/src/services/tools/manage_scene.py

61-61: Do not catch blind exception: Exception

(BLE001)

Server/src/services/tools/utils.py

76-76: Do not catch blind exception: Exception

(BLE001)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: nl-suite
🔇 Additional comments (18)
Server/src/main.py (2)

11-35: LGTM - Startup safety guard for typing names.

The workaround is well-commented and appropriately defensive. The bare except Exception: pass is intentional here as a startup safety guard that should never break server initialization.


271-283: Well-documented paging guidance.

The payload sizing instructions provide clear, actionable recommendations for LLM callers to avoid large responses. The recommended page sizes align with the PR objectives.

MCPForUnity/Editor/Clients/McpClientConfiguratorBase.cs (1)

544-549: LGTM - Dev flags correctly integrated into manual snippet.

The generated CLI command now includes --no-cache --refresh flags when dev mode is enabled, maintaining consistency with the Register path.

Server/src/services/tools/utils.py (1)

63-77: Well-designed coercion utility.

The function correctly handles edge cases: booleans (which are int subclasses in Python) return default, empty/null strings return default, and float-like strings are truncated to int. The bare except Exception is acceptable for a defensive utility.

Server/src/services/tools/read_console.py (1)

62-62: LGTM - Centralized integer coercion.

The change correctly uses the shared coerce_int utility for the count parameter.

MCPForUnity/Editor/Helpers/CodexConfigHelper.cs (2)

20-38: Well-structured dev-mode helpers.

The GetDevModeForceRefresh() and AddDevModeArgs() methods cleanly encapsulate the dev-mode logic with appropriate null checks and error handling. This pattern is correctly reused in both BuildCodexServerBlock and CreateUnityMcpTable.


62-67: LGTM - Dev flags correctly integrated.

The dev-mode flags are prepended before --from and package name, maintaining correct uvx argument ordering.

Server/tests/integration/test_manage_gameobject_param_coercion.py (2)

35-36: LGTM - Updated assertions reflect proper boolean coercion.

The assertions now correctly expect True and False boolean values rather than string representations, aligning with the centralized coerce_bool behavior.


39-72: Good test coverage for paging parameter coercion.

The new test validates that string inputs are correctly coerced to integers ("25"25, "50"50, "100"100) and booleans ("true"True). The unused cmd and kwargs parameters in fake_send are intentional to match the mocked function signature.

MCPForUnity/Editor/Helpers/ConfigJsonBuilder.cs (2)

84-87: LGTM - Dev mode flag integration.

The pattern is consistent with other configurators. The try/catch guards against potential EditorPrefs access issues.


155-177: Well-documented argument construction.

The comment clearly explains the purpose of --no-cache and --refresh flags. The argument ordering (dev flags → --from → package → --transport) is consistent across all uvx builders.

Server/src/services/tools/manage_asset.py (4)

12-12: LGTM: Centralized coercion utility imported.

The coerce_int import from utils is used at lines 92-93 for consistent numeric parameter handling across the codebase.


18-22: LGTM: Documentation enhancements improve LLM guidance.

The updated parameter descriptions and tool-level payload safety tips effectively guide callers toward safer patterns (paging, avoiding previews, using search_pattern with Assets scope).

Also applies to: 27-27, 35-39, 44-46


92-93: LGTM: Consistent numeric coercion.

Using the centralized coerce_int utility aligns with the project's pattern for handling loosely-typed numeric inputs.


95-117: Normalization logic is sound, but defensive error handling appears unnecessary.

The normalization effectively prevents token-heavy project-wide searches by moving AssetDatabase queries (e.g., "t:MonoScript") from path into search_pattern. However, the try-except block at lines 101-105 catches AttributeError and TypeError as defensive handling for non-string path values, despite path being strictly annotated as str (line 27). All test calls in the codebase pass proper string values ("Assets", "Assets/Test.mat"), and there is no evidence of None or non-string values being passed at runtime.

The defensive (path or "") pattern and error handling are overly cautious but do not harm correctness. If path type safety is guaranteed by upstream validation, the try-except can be simplified or removed.

Server/src/services/tools/manage_scene.py (3)

6-6: LGTM: Centralized coercion utility imported.

The coerce_int import is appropriately used for numeric parameter coercion.


31-38: LGTM: Paging and safety parameters enhance payload control.

The new parameters provide the necessary controls for bounded responses as described in the PR objectives.


87-100: LGTM: Conditional parameter inclusion is consistent.

The pattern of conditionally including coerced paging/safety parameters aligns with the existing approach for other optional parameters.

Copy link

@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 (2)
Server/src/services/tools/manage_scene.py (2)

6-6: Excellent refactoring: centralized utilities imported.

The import now includes coerce_bool from the centralized utilities module, addressing the code duplication concern from the previous review.


44-51: Refactoring complete: all previous concerns resolved.

This refactoring successfully addresses all three issues flagged in the previous review:

  1. ✓ Removed the redundant _coerce_int wrapper—now calls coerce_int directly
  2. ✓ Eliminated duplicate _coerce_bool logic—now uses centralized coerce_bool from utils.py
  3. ✓ Removed local broad exception handling—delegated to centralized utilities

The code is now DRY, maintainable, and consistent with the project's coercion patterns.

Based on the resolution of past review comments.

🧹 Nitpick comments (2)
MCPForUnity/Editor/Clients/McpClientConfiguratorBase.cs (1)

553-557: Extract duplicated helper to a shared utility class.

This method is duplicated verbatim in CodexConfigHelper.cs (lines 19-29). Consider extracting it to a shared helper class (e.g., EditorPrefsHelper or DevModeHelper) to follow the DRY principle and reduce maintenance burden.

💡 Suggested refactor

Create a shared utility class:

// MCPForUnity/Editor/Helpers/EditorPrefsHelper.cs
namespace MCPForUnity.Editor.Helpers
{
    internal static class EditorPrefsHelper
    {
        internal static bool GetDevModeForceRefresh()
        {
            try { return EditorPrefs.GetBool(EditorPrefKeys.DevModeForceServerRefresh, false); }
            catch { return false; }
        }
    }
}

Then replace both occurrences with:

-        private static bool GetDevModeForceRefresh()
-        {
-            try { return EditorPrefs.GetBool(EditorPrefKeys.DevModeForceServerRefresh, false); }
-            catch { return false; }
-        }
+        // Use shared helper: EditorPrefsHelper.GetDevModeForceRefresh()

Update call sites at lines 426 and 542:

-                bool devForceRefresh = GetDevModeForceRefresh();
+                bool devForceRefresh = EditorPrefsHelper.GetDevModeForceRefresh();
Server/src/services/tools/utils.py (1)

63-77: Consider narrowing the exception handler for better error visibility.

The implementation is correct and handles the defensive coercion use case well. However, the broad except Exception could mask unexpected errors during development or debugging.

🔎 Suggested refactor to catch specific exceptions
 def coerce_int(value: Any, default: int | None = None) -> int | None:
     """Attempt to coerce a loosely-typed value to an integer."""
     if value is None:
         return default
     try:
         if isinstance(value, bool):
             return default
         if isinstance(value, int):
             return value
         s = str(value).strip()
         if s.lower() in ("", "none", "null"):
             return default
         return int(float(s))
-    except Exception:
+    except (ValueError, TypeError, AttributeError):
         return default

This narrows the catch to the specific exceptions that int(), float(), str(), and .strip() would raise, improving debuggability without sacrificing robustness.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b18cd93 and 2482801.

📒 Files selected for processing (6)
  • MCPForUnity/Editor/Clients/McpClientConfiguratorBase.cs
  • Server/src/services/tools/manage_scene.py
  • Server/src/services/tools/read_console.py
  • Server/src/services/tools/run_tests.py
  • Server/src/services/tools/utils.py
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Tools/ManageScriptableObjectTests.cs
🚧 Files skipped from review as they are similar to previous changes (2)
  • Server/src/services/tools/run_tests.py
  • Server/src/services/tools/read_console.py
🧰 Additional context used
🧬 Code graph analysis (2)
MCPForUnity/Editor/Clients/McpClientConfiguratorBase.cs (3)
MCPForUnity/Editor/Helpers/CodexConfigHelper.cs (1)
  • GetDevModeForceRefresh (20-30)
MCPForUnity/Editor/Helpers/AssetPathUtility.cs (1)
  • uvxPath (169-176)
MCPForUnity/Editor/Constants/EditorPrefKeys.cs (1)
  • EditorPrefKeys (7-49)
Server/src/services/tools/manage_scene.py (1)
Server/src/services/tools/utils.py (2)
  • coerce_int (63-77)
  • coerce_bool (11-24)
🪛 Ruff (0.14.10)
Server/src/services/tools/utils.py

76-76: Do not catch blind exception: Exception

(BLE001)

🔇 Additional comments (7)
MCPForUnity/Editor/Clients/McpClientConfiguratorBase.cs (2)

426-428: LGTM - Dev mode flags correctly injected into stdio command.

The conditional devFlags construction and injection logic is correct. The trailing space in the devFlags string ensures proper spacing in the final command.


542-546: Consistent manual snippet generation.

The devFlags logic mirrors the Register() method implementation, ensuring consistency between automatic registration and manual instructions.

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

31-38: LGTM: paging/safety parameters well-documented.

The new parameters are properly typed and annotated with clear descriptions. The mix of int | str annotations aligns with the defensive coercion strategy used throughout the codebase.


64-79: LGTM: conditional parameter passing is correct.

The conditional logic correctly adds paging/safety parameters only when they are non-None after coercion. Note that parent (line 66-67) is intentionally passed through without coercion since it can be either a string (name/path) or an integer (instanceID).

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

6-6: LGTM!

The System.Threading namespace is correctly added to support Thread.Sleep in the new helper method.


26-26: Good addition for test stability.

The two WaitForUnityReady() calls each serve distinct purposes:

  • Line 26: Ensures Unity is ready before test setup begins
  • Line 50: Waits for AssetDatabase.Refresh() to complete, as it can trigger asynchronous asset processing

This addresses the intermittent setup failures mentioned in the code comments and aligns with the test stability goals in the PR objectives.

Also applies to: 50-50


311-325: Well-implemented helper for EditMode test synchronization.

The implementation is sound:

  • Uses Thread.Sleep polling, which is a pragmatic choice for EditMode tests where Unity's compilation and asset processing run on background threads
  • 30-second timeout provides a reasonable safety net for compilation scenarios
  • 50ms poll interval balances responsiveness with CPU usage
  • Clear comments explain the purpose and context

While Thread.Sleep is generally not ideal in Unity Editor code, it's acceptable here because EditorApplication.isCompiling and isUpdating are updated by Unity's background systems, allowing the main thread to safely poll these flags.

* Fix test teardown to avoid dropping MCP bridge

CodexConfigHelperTests was calling MCPServiceLocator.Reset() in TearDown, which disposes the active bridge/transport during MCP-driven test runs. Replace with restoring only the mutated service (IPlatformService).

* Avoid leaking PlatformService in CodexConfigHelperTests

Capture the original IPlatformService before this fixture runs and restore it in TearDown. This preserves the MCP connection safety fix (no MCPServiceLocator.Reset()) while avoiding global state leakage to subsequent tests.

* Fix SO MCP tooling: validate folder roots, normalize paths, expand tests; remove vestigial SO tools

* Remove UnityMCPTests stress artifacts and ignore Assets/Temp

* Ignore UnityMCPTests Assets/Temp only

* Clarify array_resize fallback logic comments

* Refactor: simplify action set and reuse slash sanitization

* Enhance: preserve GUID on overwrite & support Vector/Color types in ScriptableObject tools

* Fix: ensure asset name matches filename to suppress Unity warnings

* Fix: resolve Unity warnings by ensuring asset name match and removing redundant import

* Refactor: Validate assetName, strict object parsing for vectors, remove broken SO logic from ManageAsset

* Hardening: reject Windows drive paths; clarify supported asset types

* Delete FixscriptableobjecPlan.md

* docs: Add manage_scriptable_object tool description to README
Copy link

@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 (1)
README.md (1)

51-51: Well-placed addition to the tools list.

The new manage_scriptable_object entry is clearly described, follows the established documentation format, and is positioned appropriately within the tools section.

For future consideration: If this tool has complex parameters or usage patterns (e.g., specific SerializedObject property path syntax, constraints on asset types), consider expanding the main Tools section with a brief usage note or linking to detailed docs (e.g., a section in CUSTOM_TOOLS.md or a new docs/MANAGE_SCRIPTABLE_OBJECT.md). This could help LLMs and users understand constraints and best practices upfront—especially given the PR's focus on "payload-sizing and paging guidance" and "normalizing unsafe calls."

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2482801 and 9aa02d2.

📒 Files selected for processing (2)
  • README.md
  • TestProjects/UnityMCPTests/Assets/Tests/EditMode/Helpers/CodexConfigHelperTests.cs
🧰 Additional context used
🧬 Code graph analysis (1)
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Helpers/CodexConfigHelperTests.cs (2)
MCPForUnity/Editor/Constants/EditorPrefKeys.cs (1)
  • EditorPrefKeys (7-49)
MCPForUnity/Editor/Services/MCPServiceLocator.cs (2)
  • MCPServiceLocator (11-91)
  • Register (40-62)
🔇 Additional comments (2)
TestProjects/UnityMCPTests/Assets/Tests/EditMode/Helpers/CodexConfigHelperTests.cs (2)

35-36: LGTM! Clean DevModeForceServerRefresh handling.

The DevModeForceServerRefresh flag handling follows the established pattern for EditorPrefs state management in this fixture. The SetUp comment clearly explains why forcing the flag to false is necessary (to ensure deterministic uvx argument ordering regardless of developer settings), and the OneTimeTearDown properly restores the original state.

Also applies to: 46-47, 58-60, 102-110


76-79: Good defensive fallback for null platform service.

The else clause provides a sensible default by registering a new PlatformService() when the original was null. This ensures subsequent tests/fixtures always have a valid platform service available, which is a reasonable design choice given the fixture's goal of minimizing disruption to active MCP sessions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants