Skip to content

Bug-Fix and Doc-Update#934

Merged
Scriptwonder merged 3 commits intoCoplayDev:betafrom
Scriptwonder:bug-fix-and-doc-update-0315
Mar 15, 2026
Merged

Bug-Fix and Doc-Update#934
Scriptwonder merged 3 commits intoCoplayDev:betafrom
Scriptwonder:bug-fix-and-doc-update-0315

Conversation

@Scriptwonder
Copy link
Collaborator

@Scriptwonder Scriptwonder commented Mar 15, 2026

  1. ManagePrefabs.cs — Added PersistRuntimeMaterials() to save in-memory materials as .mat assets before prefab creation, so colors/materials survive serialization.
  2. ManageMaterial.cs — Reverted the ExtractMaterialPath helper since the Python layer already normalizes material_path → materialPath before sending to C#.
  3. GraphicsHelpers.cs — Replaced inline long parsing with ParamCoercion.CoerceLong for cleaner int/long handling in serialized property read/write.
  4. ParamCoercion.cs — Added CoerceLong() method following the same pattern as CoerceInt. Thanks to mentions at ManageScriptableObject 应该支持long类型 #928
  5. ComponentOps.cs — Use CoerceLong()
  6. ManageScriptableObject.cs — Use CoerceLong()
  7. ManageComponents.cs — Removed dead value_verified = true field from success response.
  8. ManageEditor.cs — Added close_prefab_stage action, removed unused waitForCompletion parameter.
  9. CLAUDE.md — Fixed stdio/SSE → stdio/HTTP in the architecture diagram. Thanks to mentions at Improve CLAUDE.md with ports, domains, transport modes, and resources clarification #916
  10. Skill docs — Synced all 5 .claude/skills/ files from root unity-mcp-skill/ (were stale — missing graphics, packages, camera screenshots, etc.) and added close_prefab_stage to tools-reference.

Summary by Sourcery

Persist runtime materials when creating prefabs, improve serialized int/long handling and editor/prefab workflows, and expand MCP skill documentation for graphics, packages, and script compilation behavior.

Bug Fixes:

  • Ensure runtime-only renderer materials and MaterialPropertyBlock color overrides are saved as .mat assets during prefab creation so visual changes survive serialization.
  • Add verification for object reference serialized property sets to surface failures when references do not persist.
  • Handle long-backed SerializedProperty integers correctly across components, scriptable objects, and graphics helpers using shared coercion logic.
  • Automatically inject required editor-extension-mode attribute into generated UXML so UI Builder can open created/updated UI files.
  • Clarify prefab asset vs. instance editing/instantiation paths and provide an action to exit prefab editing mode from MCP.

Enhancements:

  • Standardize long coercion via a new ParamCoercion.CoerceLong helper and use it in serialized property setters.
  • Refine manage_ui capture error responses to include structured retry hints.
  • Document and expose new manage_editor close_prefab_stage action across C# and Python tooling.

Documentation:

  • Update CLAUDE.md to reflect stdio/HTTP transport, clarify architecture layers, tool/resource registration patterns, and testing/extension workflows.
  • Revise SKILL and workflows references to emphasize automatic script compilation, prefer manage_camera for screenshots, and document prefab instantiation via manage_gameobject.
  • Add detailed documentation for new graphics and package management tools and workflows, including batch discovery examples and updated tool reference sections.
  • Sync and expand Unity MCP skill reference files with current capabilities, including graphics, package, and editor deployment tooling.

Summary by CodeRabbit

  • New Features

    • New graphics and package workflows, deployment guidance, batch operation patterns, and a new editor action to exit prefab-edit mode; script validation tooling added.
  • Documentation

    • Emphasized waiting for compilation (auto-triggered on script create/edit); prefabs now documented to instantiate via gameobject create; screenshot examples switched to camera-based calls; added batch_execute and prefab-instantiation examples.
  • Bug Fixes

    • Improved integer/long handling, prefab material persistence, property readback verification, and UI/content validation.

Copilot AI review requested due to automatic review settings March 15, 2026 20:16
@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Mar 15, 2026

Reviewer's Guide

Implements a prefab material persistence fix, improves serialized int/long handling, adds a close_prefab_stage editor action, and updates MCP Unity skill/docs for new graphics, packages, scripting, and screenshot workflows.

Sequence diagram for prefab creation with runtime material persistence

sequenceDiagram
    actor EditorUser
    participant ManagePrefabs
    participant PersistRuntimeMaterials
    participant Renderer
    participant AssetDatabase
    participant PrefabStage as CreatePrefabAsset

    EditorUser->>ManagePrefabs: manage_prefabs(action="create_from_gameobject", ...)
    ManagePrefabs->>ManagePrefabs: Validate parameters and sourceObject

    Note over ManagePrefabs: Step 7: Persist runtime-only materials
    ManagePrefabs->>PersistRuntimeMaterials: PersistRuntimeMaterials(root, prefabPath)
    PersistRuntimeMaterials->>Renderer: GetComponentsInChildren<Renderer>(includeInactive=true)
    loop For each renderer
        PersistRuntimeMaterials->>Renderer: Get sharedMaterials
        loop For each material slot
            PersistRuntimeMaterials->>Renderer: Check material instance and property block
            alt Runtime instance or property block colors
                PersistRuntimeMaterials->>AssetDatabase: AssetPathUtility.SanitizeAssetPath(matPath)
                PersistRuntimeMaterials->>AssetDatabase: IsValidFolder(materialsFolder)
                alt Folder missing
                    PersistRuntimeMaterials->>AssetDatabase: CreateFolder(parentDir, folderName)
                end
                PersistRuntimeMaterials->>AssetDatabase: LoadAssetAtPath<Material>(matPath)
                alt Material asset missing
                    PersistRuntimeMaterials->>RenderPipeline as RenderPipelineUtility: ResolveShader("Standard")
                    PersistRuntimeMaterials->>AssetDatabase: CreateAsset(new Material(shader), matPath)
                end
                PersistRuntimeMaterials->>Renderer: Apply runtime properties to persisted material
                PersistRuntimeMaterials->>Renderer: Replace sharedMaterials[slot] with persisted asset
            end
        end
        PersistRuntimeMaterials->>Renderer: Undo.RecordObject(renderer, "Persist runtime materials for prefab")
        PersistRuntimeMaterials->>Renderer: Clear property blocks
        PersistRuntimeMaterials->>AssetDatabase: SaveAssets
    end
    PersistRuntimeMaterials-->>ManagePrefabs: (count, paths)

    Note over ManagePrefabs,PrefabStage: Step 8: Create prefab asset
    ManagePrefabs->>PrefabStage: CreatePrefabAsset(sourceObject, prefabPath, replaceExisting)
    PrefabStage-->>ManagePrefabs: GameObject prefabInstance

    alt Prefab creation succeeded
        ManagePrefabs->>ManagePrefabs: Build SuccessResponse
        ManagePrefabs-->>EditorUser: { wasCreated, wasReplaced, childCount, materialsPersisted }
    else Prefab creation failed
        ManagePrefabs-->>EditorUser: ErrorResponse("Failed to create prefab asset")
    end
Loading

Sequence diagram for manage_editor close_prefab_stage tool from Python to C#

sequenceDiagram
    actor MCPClient
    participant PythonTool as manage_editor.py
    participant UnityTransport as send_with_unity_instance
    participant CSharpTool as ManageEditor.HandleCommand
    participant PrefabAPI as PrefabStageUtility/StageUtility

    MCPClient->>PythonTool: manage_editor(action="close_prefab_stage")
    PythonTool->>UnityTransport: async_send_command_with_retry("manage_editor", { action: close_prefab_stage })
    UnityTransport->>CSharpTool: HandleCommand(params)

    CSharpTool->>CSharpTool: Read action from ToolParams
    CSharpTool->>PrefabAPI: GetCurrentPrefabStage()
    alt In prefab stage
        PrefabAPI-->>CSharpTool: prefabStage (with assetPath)
        CSharpTool->>PrefabAPI: GoToMainStage()
        CSharpTool-->>UnityTransport: SuccessResponse({ message, prefabPath })
    else Not in prefab stage
        PrefabAPI-->>CSharpTool: null
        CSharpTool-->>UnityTransport: SuccessResponse({ message: "Not currently in prefab editing mode." })
    end

    UnityTransport-->>PythonTool: SuccessResponse
    PythonTool-->>MCPClient: Result payload (message, optional prefabPath)
Loading

Class diagram for prefab material persistence and serialized int/long handling

classDiagram
    class ManagePrefabs {
        +object HandleCommand(JObject @params)
        -static object CreatePrefabFromGameObject(JObject @params)
        -static (int count, List~string~ paths) PersistRuntimeMaterials(GameObject root, string prefabPath)
        -static bool HasPropertyBlockColors(Renderer renderer, int slot)
        -static void ApplyPropertyBlockToMaterial(Renderer renderer, int slot, Material mat)
    }

    class ParamCoercion {
        +static int CoerceInt(JToken token, int defaultValue)
        +static long CoerceLong(JToken token, long defaultValue)
        +static bool CoerceBool(JToken token, bool defaultValue)
        +static float CoerceFloat(JToken token, float defaultValue)
    }

    class GraphicsHelpers {
        +static object ReadSerializedValue(SerializedProperty prop)
        +static bool SetSerializedValue(SerializedProperty prop, JToken value)
    }

    class ComponentOps {
        -static bool SetViaSerializedProperty(Component component, string propertyName, string normalizedName, JToken value, ref string error)
        -static bool SetSerializedPropertyRecursive(SerializedProperty prop, JToken value, ref string error)
    }

    class ManageScriptableObject {
        -static bool TrySetValueRecursive(SerializedProperty prop, JToken valueToken, ref string message)
    }

    class ManageEditor {
        +object HandleCommand(JObject @params)
        -static object ClosePrefabStage()
        -static object DeployPackage()
        -static object RestorePackage()
    }

    class UnityEditor_APIs {
        <<utility>>
        PrefabStageUtility
        StageUtility
        AssetDatabase
        EditorUtility
        Undo
    }

    ManagePrefabs --> UnityEditor_APIs : uses
    ManagePrefabs ..> ParamCoercion : (indirect via materials)

    GraphicsHelpers ..> ParamCoercion : uses
    ComponentOps ..> ParamCoercion : uses
    ManageScriptableObject ..> ParamCoercion : uses

    ManageEditor --> UnityEditor_APIs : uses PrefabStageUtility, StageUtility

    ManagePrefabs --> Renderer : scans and updates
    ManagePrefabs --> Material : creates and persists
Loading

Flow diagram for manage_editor close_prefab_stage action

flowchart TD
    Start["manage_editor(action=close_prefab_stage)"] --> GetStage["GetCurrentPrefabStage()
via PrefabStageUtility"]

    GetStage -->|"prefabStage is null"| NotInStage["Return SuccessResponse
'Not currently in prefab editing mode.'"]

    GetStage -->|"prefabStage not null"| ReadPath["prefabPath = prefabStage.assetPath"]
    ReadPath --> ExitStage["StageUtility.GoToMainStage()
(exit prefab editing)"]
    ExitStage --> ReturnSuccess["Return SuccessResponse
with prefabPath in data"]

    GetStage --> Err["Exception thrown"]
    Err --> ReturnError["Return ErrorResponse
'Error closing prefab stage: ...'"]
Loading

File-Level Changes

Change Details Files
Persist runtime-only materials before prefab creation so colors survive serialization.
  • Add PersistRuntimeMaterials helper to scan renderers for runtime-only or property-block-only materials and save them as .mat assets next to the prefab.
  • Wire PersistRuntimeMaterials into CreatePrefabFromGameObject and return materialsPersisted metadata in the success payload.
  • Implement helper methods to detect MaterialPropertyBlock color data and transfer it onto newly created material assets.
MCPForUnity/Editor/Tools/Prefabs/ManagePrefabs.cs
Standardize serialized int/long coercion and add better verification for object reference properties.
  • Introduce ParamCoercion.CoerceLong mirroring CoerceInt, handling integers, strings, and floats.
  • Use CoerceLong for SerializedPropertyType.Integer handling in GraphicsHelpers, ComponentOps, and ManageScriptableObject when the underlying field type is long.
  • Tighten integer null/validation logic for serialized properties and distinguish between int vs long messages in ManageScriptableObject.
  • Add readback verification for SerializedPropertyType.ObjectReference in ComponentOps so failed reference assignments return a clear error.
MCPForUnity/Editor/Helpers/ParamCoercion.cs
MCPForUnity/Editor/Helpers/ComponentOps.cs
MCPForUnity/Editor/Tools/Graphics/GraphicsHelpers.cs
MCPForUnity/Editor/Tools/ManageScriptableObject.cs
Extend manage_editor with close_prefab_stage and simplify its API on both C# and Python sides.
  • Add close_prefab_stage action in ManageEditor to exit prefab editing mode via PrefabStageUtility/StageUtility and return the prefab path.
  • Remove unused waitForCompletion parameter from ManageEditor C# handler and corresponding wait_for_completion plumbing from manage_editor MCP tool.
  • Update error messaging and tool descriptions to mention close_prefab_stage and clarify supported actions.
MCPForUnity/Editor/Tools/ManageEditor.cs
Server/src/services/tools/manage_editor.py
Make UI creation safer by enforcing editor-extension-mode on generated/updated UXML and improving render_ui contention errors.
  • Ensure new or updated UXML contents always include editor-extension-mode="False" on the root UXML element so UI Builder can open them.
  • Refine render_ui capture-in-progress error to a structured retryable ErrorResponse with retry_after_ms and reason fields.
MCPForUnity/Editor/Tools/ManageUI.cs
Clarify prefab vs scene operations, scripting/compilation workflow, and screenshot/camera usage in skills and references.
  • Document that prefab instantiation should use manage_gameobject(action="create", prefab_path=...) and add examples in tools-reference and workflows docs.
  • Adjust manage_gameobject error text for prefab-assets to point to manage_prefabs modify_contents and manage_editor close_prefab_stage.
  • Update SKILL.md and workflow docs to emphasize waiting on mcpforunity://editor/state instead of calling refresh_unity after create_script/script_apply_edits, and to use manage_camera for screenshots.
  • Add batch_execute discovery examples for multi-search and update tables describing script, asset, graphics, and packages tools (including manage_packages as the single package tool).
unity-mcp-skill/SKILL.md
unity-mcp-skill/references/workflows.md
.claude/skills/unity-mcp-skill/SKILL.md
.claude/skills/unity-mcp-skill/references/workflows.md
unity-mcp-skill/references/tools-reference.md
.claude/skills/unity-mcp-skill/references/tools-reference.md
MCPForUnity/Editor/Tools/GameObjects/ManageGameObject.cs
Expand graphics and package management documentation and sync skill docs/diagrams with current architecture.
  • Add comprehensive Graphics & Rendering, Package Management, and Package Deployment workflow sections to the Unity MCP skill docs, including manage_graphics and manage_packages usage patterns.
  • Introduce detailed graphics-tools and package-tools sections in tools-reference, listing parameters, actions, and examples for manage_graphics and manage_packages.
  • Refresh CLAUDE.md to document stdio vs HTTP transports, MCP vs CLI/tool layers, registration patterns for MCP/C# tools/resources, and updated testing/feature-adding guidance, including the stdio/HTTP diagram fix.
  • Sync the .claude skill copies of SKILL.md and references with the root unity-mcp-skill versions so they’re no longer stale.
.claude/skills/unity-mcp-skill/references/workflows.md
.claude/skills/unity-mcp-skill/references/tools-reference.md
.claude/skills/unity-mcp-skill/SKILL.md
unity-mcp-skill/references/workflows.md
unity-mcp-skill/references/tools-reference.md
unity-mcp-skill/SKILL.md
CLAUDE.md

Possibly linked issues

  • #: Yes. PR implements the requested Unity MCP Claude skill guidance, including refresh_unity alternatives, batch_execute, and tool schemas.

Tips and commands

Interacting with Sourcery

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

Customizing Your Experience

Access your dashboard to:

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

Getting Help

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 15, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 842499c8-c0c8-489e-b042-a01329289ec7

📥 Commits

Reviewing files that changed from the base of the PR and between c304054 and 33892fb.

📒 Files selected for processing (1)
  • MCPForUnity/Editor/Tools/Prefabs/ManagePrefabs.cs

📝 Walkthrough

Walkthrough

This PR updates docs and the Unity MCP skill, adds prefab/material persistence and editor actions, tightens integer/long coercion and SerializedProperty handling, changes screenshot tooling to camera-based calls, and adjusts script compilation/workflow behavior (auto-refresh + wait-for-compilation). It also removes wait_for_completion from manage_editor and adds close_prefab_stage.

Changes

Cohort / File(s) Summary
Docs & Skill Metadata
\.claude/skills/unity-mcp-skill/SKILL.md, \.claude/skills/unity-mcp-skill/references/tools-reference.md, \.claude/skills/unity-mcp-skill/references/workflows.md, unity-mcp-skill/SKILL.md, unity-mcp-skill/references/tools-reference.md, unity-mcp-skill/references/workflows.md, CLAUDE.md
Replaced manage_scene screenshot examples with manage_camera, changed Scripts tools list to use validate_script (removed explicit refresh_unity), documented prefab instantiation via manage_gameobject(action="create", prefab_path=...), added batch_execute examples, and shifted refresh steps to “wait for compilation” polls.
Prefab & Editor Controls
MCPForUnity/Editor/Tools/Prefabs/ManagePrefabs.cs, MCPForUnity/Editor/Tools/GameObjects/ManageGameObject.cs, MCPForUnity/Editor/Tools/ManageEditor.cs, Server/src/services/tools/manage_editor.py
Persist runtime materials when creating prefabs (new helpers and response field), added ClosePrefabStage/close_prefab_stage to exit prefab stage, updated prefab-related error messaging, and removed wait_for_completion parameter from Python manage_editor.
Property Coercion & Serialization
MCPForUnity/Editor/Helpers/ParamCoercion.cs, MCPForUnity/Editor/Helpers/ComponentOps.cs, MCPForUnity/Editor/Tools/GraphicsHelpers.cs, MCPForUnity/Editor/Tools/ManageScriptableObject.cs
Added CoerceLong and explicit long handling, tightened null checks for integer inputs, and switch between prop.longValue and prop.intValue based on prop.type=="long".
Material Assignment & CLI
MCPForUnity/Editor/Tools/ManageMaterial.cs, Server/src/cli/commands/material.py, Server/src/services/tools/manage_material.py
Default SetRendererColor mode changed to property_block; added EnsureAssetFolderExists for recursive folder creation; updated CLI default/help text and manage_material mode description.
UI & UXML Helpers
MCPForUnity/Editor/Tools/ManageUI.cs, MCPForUnity/Editor/Windows/Components/Advanced/McpAdvancedSection.uxml
Injected EnsureEditorExtensionMode to ensure root UXML has editor-extension-mode attribute; made play-capture conflict error include retry metadata; small UI label tweak.
Misc docs / rendering & package tools
\.claude/.../references/*, unity-mcp-skill/references/*, MCPForUnity/Editor/Tools/Graphics/...
Added Graphics Tools and Package Tools docs, expanded graphics/rendering and package workflows, and duplicated graphics content across editor/camera sections for consistency.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant Client as Client
participant Server as Server
participant Unity as UnityEditor
Client->>Server: POST manage_editor(action="close_prefab_stage")
Server->>Unity: invoke ClosePrefabStage()
Unity-->>Server: { status: success/none, prefab_path? }
Server-->>Client: return response

mermaid
sequenceDiagram
participant Client as Client
participant Server as Server
participant Unity as UnityEditor
participant AssetDB as AssetDB
Client->>Server: POST manage_prefabs(action="create", sourceObject, path)
Server->>Unity: Begin PersistRuntimeMaterials(root, prefabPath)
Unity->>AssetDB: create/update Material assets under Materials/...
AssetDB-->>Unity: return asset paths
Unity->>Unity: update renderer.sharedMaterials, clear property blocks
Unity->>Unity: create prefab asset
Unity-->>Server: { prefab_created, materialsPersisted }
Server-->>Client: return response

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I hopped through code with quick delight,
Long numbers snug and materials tight,
Prefab doors close with a gentle click,
Cameras snap, docs hum a new trick,
A tidy garden of functions — nibble and night 🥕

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 46.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Bug-Fix and Doc-Update' is vague and generic, using non-descriptive terms that do not clearly convey the primary change or scope of the changeset. Use a more specific title that highlights the main change (e.g., 'Persist runtime materials in prefabs and update documentation' or 'Add prefab material persistence and expand MCP skill docs').
✅ Passed checks (1 passed)
Check name Status Explanation
Description check ✅ Passed The description is comprehensive and well-organized, covering bug fixes, enhancements, and documentation updates with specific file changes and clear explanations of improvements.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
📝 Coding Plan
  • Generate coding plan for human review comments

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

❤️ Share

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

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 1 issue, and left some high level feedback:

  • The new integer handling in ComponentOps.SetSerializedPropertyRecursive and ManageScriptableObject.TrySetValueRecursive now treats null/missing values as hard errors; if some integer fields are meant to be optional or left unchanged, consider preserving the previous "no-op on null" behavior instead of failing the whole set.
  • In ManageEditor.ClosePrefabStage, the catch block returns an error message but drops the original exception; consider logging the exception (e.g., via McpLog.Error) to aid debugging when prefab stage transitions fail.
  • The EnsureEditorExtensionMode helper performs string-based injection into the UXML root tag; to avoid edge cases with XML declarations, comments, or unconventional formatting, consider switching to an XML-based approach (e.g., XDocument) so the attribute is added in a more robust and schema-safe way.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The new integer handling in `ComponentOps.SetSerializedPropertyRecursive` and `ManageScriptableObject.TrySetValueRecursive` now treats `null`/missing values as hard errors; if some integer fields are meant to be optional or left unchanged, consider preserving the previous "no-op on null" behavior instead of failing the whole set.
- In `ManageEditor.ClosePrefabStage`, the catch block returns an error message but drops the original exception; consider logging the exception (e.g., via `McpLog.Error`) to aid debugging when prefab stage transitions fail.
- The `EnsureEditorExtensionMode` helper performs string-based injection into the UXML root tag; to avoid edge cases with XML declarations, comments, or unconventional formatting, consider switching to an XML-based approach (e.g., `XDocument`) so the attribute is added in a more robust and schema-safe way.

## Individual Comments

### Comment 1
<location path="MCPForUnity/Editor/Helpers/ComponentOps.cs" line_range="543-552" />
<code_context>
                         prop.boolValue = ParamCoercion.CoerceBool(value, false);
                         return true;
                     case SerializedPropertyType.Integer:
-                        prop.intValue = ParamCoercion.CoerceInt(value, 0);
+                        if (prop.type == "long")
+                            prop.longValue = ParamCoercion.CoerceLong(value, 0);
</code_context>
<issue_to_address>
**issue (bug_risk):** Integer coercion now silently maps invalid inputs to 0 instead of surfacing an error.

The previous approach used `int.MinValue` as a sentinel and surfaced an error on parse failure. Now, anything non-null is passed to `CoerceInt`/`CoerceLong` with a default of 0, so values like "abc" or malformed numbers become 0 instead of failing. This can hide data issues and unintentionally overwrite values with 0. Please add a way to detect coercion failures (so you can distinguish a real 0 from a failed parse) and apply the same fix in `ManageScriptableObject.TrySetValueRecursive` for consistency.
</issue_to_address>

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

Copy link
Contributor

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 improves Unity-side tooling correctness (prefab/material persistence and int64 serialized property handling), adds an editor control action for exiting Prefab Stage, and refreshes contributor/skill documentation to match current tool behavior and transport architecture.

Changes:

  • Persist runtime-only materials to .mat assets during prefab creation so material appearance survives serialization.
  • Add ParamCoercion.CoerceLong() and update serialized-property read/write helpers to properly handle long fields (avoiding int32 truncation).
  • Add manage_editor(action="close_prefab_stage"), remove the unused waitForCompletion parameter, and sync/refresh skill + architecture docs.

Reviewed changes

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

Show a summary per file
File Description
unity-mcp-skill/references/workflows.md Updates script workflows to wait on compilation via editor state; adds prefab instantiation guidance; adds batch discovery note.
unity-mcp-skill/references/tools-reference.md Adds prefab instantiation example and documents close_prefab_stage.
unity-mcp-skill/SKILL.md Updates best practices around script compilation waits and prefab instantiation guidance.
Server/src/services/tools/manage_editor.py Adds close_prefab_stage action; removes wait_for_completion plumbing.
MCPForUnity/Editor/Tools/Prefabs/ManagePrefabs.cs Adds PersistRuntimeMaterials() and surfaces materialsPersisted in the response.
MCPForUnity/Editor/Tools/ManageUI.cs Injects editor-extension-mode for UXML writes; improves capture-in-progress error response payload.
MCPForUnity/Editor/Tools/ManageScriptableObject.cs Writes longValue for long-typed integer properties using CoerceLong.
MCPForUnity/Editor/Tools/ManageEditor.cs Adds close_prefab_stage action and removes unused waitForCompletion param.
MCPForUnity/Editor/Tools/Graphics/GraphicsHelpers.cs Reads/writes long via prop.longValue when applicable.
MCPForUnity/Editor/Tools/GameObjects/ManageGameObject.cs Updates prefab-asset error guidance to match current prefab tooling.
MCPForUnity/Editor/Helpers/ParamCoercion.cs Adds CoerceLong() helper.
MCPForUnity/Editor/Helpers/ComponentOps.cs Uses CoerceLong and adds ObjectReference readback verification.
CLAUDE.md Updates transport diagram and expands repo architecture/tooling guidance.
.claude/skills/unity-mcp-skill/references/workflows.md Syncs skill docs (graphics/packages/camera screenshot updates, prefab instantiation, batch discovery).
.claude/skills/unity-mcp-skill/references/tools-reference.md Syncs tool reference additions for graphics/packages and close_prefab_stage.
.claude/skills/unity-mcp-skill/SKILL.md Syncs skill best practices (camera screenshots, compilation waits, package section).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines +1858 to +1869
if (contents.Contains("editor-extension-mode"))
return contents;

int idx = contents.IndexOf("<ui:UXML", StringComparison.Ordinal);
if (idx < 0)
idx = contents.IndexOf("<UXML", StringComparison.Ordinal);
if (idx < 0)
return contents;

int closeTag = contents.IndexOf('>', idx);
if (closeTag < 0)
return contents;
// Case 2: Material exists but is not a persistent asset (runtime instance).
bool isRuntimeInstance = mat != null && !EditorUtility.IsPersistent(mat);
bool isNullWithPropertyBlock = mat == null && HasPropertyBlockColors(renderer, slot);
bool isNullMaterial = mat == null && !isNullWithPropertyBlock;
Comment on lines +302 to +306
// Derive a unique asset path from the GameObject name and slot
string goName = renderer.gameObject.name.Replace(" ", "_");
string suffix = slot > 0 ? $"_slot{slot}" : "";
string matPath = $"{materialsFolder}/{goName}{suffix}_mat.mat";
matPath = AssetPathUtility.SanitizeAssetPath(matPath);
Comment on lines +355 to +359
// Clear any property blocks now that the material is persisted
for (int slot = 0; slot < sharedMats.Length; slot++)
{
renderer.SetPropertyBlock(null, slot);
}
{
MaterialPropertyBlock block = new MaterialPropertyBlock();
renderer.GetPropertyBlock(block, slot);
return !block.isEmpty;
Comment on lines 926 to +936
case SerializedPropertyType.Integer:
// Use ParamCoercion for robust int parsing
int intVal = ParamCoercion.CoerceInt(valueToken, int.MinValue);
if (intVal == int.MinValue && valueToken?.Type != JTokenType.Integer)
if (valueToken == null || valueToken.Type == JTokenType.Null)
{
// Double-check: if it's actually int.MinValue or failed to parse
if (valueToken == null || valueToken.Type == JTokenType.Null ||
(valueToken.Type == JTokenType.String && !int.TryParse(valueToken.ToString(), out _)))
{
message = "Expected integer value.";
return false;
}
message = "Expected integer value.";
return false;
}
prop.intValue = intVal;
message = "Set int.";
if (prop.type == "long")
prop.longValue = ParamCoercion.CoerceLong(valueToken, 0);
else
prop.intValue = ParamCoercion.CoerceInt(valueToken, 0);
message = prop.type == "long" ? "Set long." : "Set int.";
Comment on lines 543 to +552
case SerializedPropertyType.Integer:
int intVal = ParamCoercion.CoerceInt(value, int.MinValue);
if (intVal == int.MinValue && value?.Type != JTokenType.Integer)
if (value == null || value.Type == JTokenType.Null)
{
if (value == null || value.Type == JTokenType.Null ||
(value.Type == JTokenType.String && !int.TryParse(value.ToString(), out _)))
{
error = "Expected integer value.";
return false;
}
error = "Expected integer value.";
return false;
}
prop.intValue = intVal;
if (prop.type == "long")
prop.longValue = ParamCoercion.CoerceLong(value, 0);
else
prop.intValue = ParamCoercion.CoerceInt(value, 0);
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: 12

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.claude/skills/unity-mcp-skill/references/workflows.md:
- Around line 119-121: Several wait loops poll the editor state using the wrong
key name "isCompiling" which should be "is_compiling"; update every occurrence
that reads mcpforunity://editor/state (notably the "manage_script update
auto-triggers import + compile" snippet and the other occurrences around the
noted ranges) to check "is_compiling" instead of "isCompiling" so the wait logic
reads the correct field; search for the symbol isCompiling and replace those
checks with is_compiling while ensuring the surrounding logic that reads
editor/state remains unchanged.

In @.claude/skills/unity-mcp-skill/SKILL.md:
- Line 198: Update the compile-wait instruction to use the documented
editor_state field name: replace the camelCase token "isCompiling" in the line
"Read mcpforunity://editor/state → wait until isCompiling == false" with the
snake_case "is_compiling" so it matches the editor_state schema and avoids
breaking integrations that expect editor_state.is_compiling.

In `@CLAUDE.md`:
- Line 40: Update the description of HTTP isolation to clearly distinguish
local/shared-server behavior from remote-hosted behavior: state that in HTTP
mode a multi-agent setup on a single shared Python server uses session isolation
via client_id for per-client sessions, and explicitly add that in remote-hosted
transport the identity is resolved from the API key to an authenticated user_id
which is used for tenant scoping; reference the existing phrase "Session
isolation via `client_id`" and add the clause about resolving API key to
`user_id` for remote-hosted scoping to remove ambiguity.

In `@MCPForUnity/Editor/Tools/ManageEditor.cs`:
- Around line 372-385: Before calling StageUtility.GoToMainStage() in
ClosePrefabStage(), check the current prefab stage for unsaved changes via
PrefabStageUtility.GetCurrentPrefabStage() and prefabStage.hasUnsavedChanges; if
true, deterministically resolve them by saving the scene with
EditorSceneManager.SaveScene(prefabStage.scene) (and handle/save failure) or
explicitly discard changes via the prefab stage API before calling
StageUtility.GoToMainStage(); update ClosePrefabStage to perform that check and
only switch stages after saving or discarding to avoid blocking in headless/CI
environments.

In `@MCPForUnity/Editor/Tools/ManageScriptableObject.cs`:
- Around line 927-937: The code currently coerces non-numeric JToken values
(e.g., "abc") to 0 via ParamCoercion.CoerceLong/CoerceInt causing silent
corruption; update the block that handles valueToken/prop.type to first validate
that valueToken represents a numeric value (JTokenType.Integer or Float) or a
numeric string that parses successfully, and if it does not, set message =
"Expected integer value." and return false; only call
ParamCoercion.CoerceLong/CoerceInt and assign prop.longValue/prop.intValue when
the token passes that numeric check, otherwise reject before coercion.

In `@MCPForUnity/Editor/Tools/ManageUI.cs`:
- Around line 184-185: The current use of
contents.Contains("editor-extension-mode") in EnsureEditorExtensionMode can
match tokens anywhere (comments/child nodes) and cause incorrect behavior;
update EnsureEditorExtensionMode (and callers that manipulate the contents
string) to only check and inject the attribute on the root UXML opening tag by
parsing or using a targeted regex that finds the first opening tag (e.g., match
the first "<...>" start tag) and then test/insert editor-extension-mode into
that tag rather than doing a global Contains on the entire contents string;
ensure the same scoped approach is applied to the other occurrences around lines
where contents is modified so the attribute is only considered/inserted on the
root element.

In `@MCPForUnity/Editor/Tools/Prefabs/ManagePrefabs.cs`:
- Around line 302-347: matPath currently uses only renderer.gameObject.name and
slot which causes different renderers with the same name to collide; change the
matPath generation in ManagePrefabs so it includes a stable unique identifier
for the prefab instance (e.g. the prefab asset path or prefab GUID or a
sanitized hierarchy path) along with renderer.gameObject.name and slot before
calling AssetPathUtility.SanitizeAssetPath. Locate the matPath build around the
variables goName, suffix and materialsFolder, obtain a prefab identifier via
PrefabUtility.GetPrefabAssetPathOfNearestInstanceRoot(renderer.gameObject) or
PrefabUtility.GetPrefabInstanceHandle/GetLocalIdentifierInFile and incorporate a
short hash or sanitized version of that identifier into the filename (or folder)
to ensure distinct persisted .mat files for different prefabs while still using
AssetPathUtility.SanitizeAssetPath on the final path.
- Around line 355-359: HasPropertyBlockColors() and
ApplyPropertyBlockToMaterial() currently treat any non-empty
MaterialPropertyBlock as a color candidate and then the code clears all slots on
the renderer (renderer.SetPropertyBlock(null, slot)), which wipes unrelated
overrides; narrow detection to only consider color properties and only clear the
specific slot after migration. Update HasPropertyBlockColors() to check for
presence of _BaseColor or _Color (use block.TryGetColor or block.HasProperty for
those names) instead of !block.isEmpty, change ApplyPropertyBlockToMaterial() to
remove/ignore only the color properties from the block and then call
renderer.SetPropertyBlock(null, slot) only for that same slot when the block no
longer contains any other properties; apply the same per-slot-only logic to the
similar block around the second occurrence (the 376-380 area) and stop clearing
every slot in sharedMats.
- Around line 295-300: The current check uses isNullWithPropertyBlock (which is
true only when mat == null) so renderers with a persistent shared material plus
a MaterialPropertyBlock color override are skipped; change the logic to detect
property-block colors regardless of mat: call HasPropertyBlockColors(renderer,
slot) into a boolean like hasPropertyBlock and replace the continue condition
with if (!isRuntimeInstance && !hasPropertyBlock) continue so property-block
tints on non-null shared materials are handled.

In `@Server/src/services/tools/manage_editor.py`:
- Line 13: The endpoint description in manage_editor.py still mentions boolean
argument handling ("Tip: pass booleans as true/false; ... 'true'/'false' are
accepted") even though the endpoint no longer accepts boolean inputs after
removing wait_for_completion; update the description string (the description
variable/docstring in this file) to remove the stale boolean-parameter guidance
and adjust the remaining sentence flow so it only documents current read-only
and modifying actions (telemetry_status, telemetry_ping, play, pause, stop,
set_active_tool, add_tag, remove_tag, add_layer, remove_layer,
close_prefab_stage, deploy_package, restore_package).

In `@unity-mcp-skill/references/workflows.md`:
- Around line 119-120: The wait snippets use a different field name
(isCompiling) than the rest of the workflow which reads
editor_state["is_compiling"]; update all wait examples (including the
manage_script update auto-triggers import+compile snippet and the other
occurrences mentioned) to reference editor_state["is_compiling"] instead of
isCompiling so they all use the same editor_state field; search for isCompiling
in the document and replace with editor_state["is_compiling"] in those wait
examples and ensure the surrounding code still references the editor_state
object consistently.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: cb807129-236c-410f-a8d4-a38f683ff8c7

📥 Commits

Reviewing files that changed from the base of the PR and between 189b983 and 0b56f0a.

📒 Files selected for processing (16)
  • .claude/skills/unity-mcp-skill/SKILL.md
  • .claude/skills/unity-mcp-skill/references/tools-reference.md
  • .claude/skills/unity-mcp-skill/references/workflows.md
  • CLAUDE.md
  • MCPForUnity/Editor/Helpers/ComponentOps.cs
  • MCPForUnity/Editor/Helpers/ParamCoercion.cs
  • MCPForUnity/Editor/Tools/GameObjects/ManageGameObject.cs
  • MCPForUnity/Editor/Tools/Graphics/GraphicsHelpers.cs
  • MCPForUnity/Editor/Tools/ManageEditor.cs
  • MCPForUnity/Editor/Tools/ManageScriptableObject.cs
  • MCPForUnity/Editor/Tools/ManageUI.cs
  • MCPForUnity/Editor/Tools/Prefabs/ManagePrefabs.cs
  • Server/src/services/tools/manage_editor.py
  • unity-mcp-skill/SKILL.md
  • unity-mcp-skill/references/tools-reference.md
  • unity-mcp-skill/references/workflows.md

### Transport Modes

- **Stdio**: Single-agent only. Separate Python process per client. Legacy TCP bridge to Unity. New connections stomp old ones.
- **HTTP**: Multi-agent ready. Single shared Python server. WebSocket hub at `/hub/plugin`. Session isolation via `client_id`.
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Clarify HTTP isolation mechanism (client_id vs authenticated user_id).

Line 40 currently says HTTP mode isolates sessions via client_id, but in remote-hosted mode the transport resolves identity from API key to user_id for scoping. Please update this line to avoid security/tenant-isolation ambiguity.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@CLAUDE.md` at line 40, Update the description of HTTP isolation to clearly
distinguish local/shared-server behavior from remote-hosted behavior: state that
in HTTP mode a multi-agent setup on a single shared Python server uses session
isolation via client_id for per-client sessions, and explicitly add that in
remote-hosted transport the identity is resolved from the API key to an
authenticated user_id which is used for tenant scoping; reference the existing
phrase "Session isolation via `client_id`" and add the clause about resolving
API key to `user_id` for remote-hosted scoping to remove ambiguity.

Comment on lines +372 to +385
private static object ClosePrefabStage()
{
try
{
var prefabStage = PrefabStageUtility.GetCurrentPrefabStage();
if (prefabStage == null)
{
return new SuccessResponse("Not currently in prefab editing mode.");
}

string prefabPath = prefabStage.assetPath;
StageUtility.GoToMainStage();
return new SuccessResponse($"Exited prefab stage for '{prefabPath}'.", new { prefabPath });
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

In Unity Editor scripting, what happens when StageUtility.GoToMainStage() is called while the current Prefab Stage has unsaved changes and Auto Save is disabled?

💡 Result:

When you call StageUtility.GoToMainStage() while you’re currently in a Prefab Stage that has unsaved changes and Auto Save is disabled, Unity tries to switch stages, but first asks permission to leave the current stage.

Concretely:

  • StageUtility.GoToMainStage() delegates the navigation to StageNavigationManager.instance.GoToMainStage(...). [1]
  • When switching stages, Unity calls previousStage.AskUserToSaveModifiedStageBeforeSwitchingStage(). If that returns false, the stage switch is canceled and you remain in the Prefab Stage. [2]
  • Because a PrefabStage reports hasUnsavedChanges when its preview scene is dirty, your Prefab Stage counts as “modified” in this situation. [3]
  • With Auto Save disabled, Unity won’t silently save on the way out; instead, the user is expected to decide (typically via a Save / Don’t Save / Cancel prompt) whether to save, discard, or cancel the navigation. Auto Save can also be entirely disallowed at the project level via EditorSettings.prefabModeAllowAutoSave. [3][4]

Net effect: the editor will block the stage switch until the user chooses what to do; if the user cancels, GoToMainStage() does not take you back to the Main Stage. [2]

Sources
[1] UnityCsReference – StageUtility.GoToMainStage() implementation (calls StageNavigationManager.instance.GoToMainStage(...)). (github.com)
[2] UnityCsReference – StageNavigationManager.SwitchToStage: calls AskUserToSaveModifiedStageBeforeSwitchingStage() and cancels switching when it returns false. (github.com)
[3] UnityCsReference – PrefabStage.hasUnsavedChanges and PrefabStage.autoSave logic. (github.com)
[4] Unity Scripting API – EditorSettings.prefabModeAllowAutoSave (project-level ability to allow/disallow Prefab Mode Auto Save UI/behavior). (docs.unity3d.com)

Citations:


Check for unsaved changes before calling StageUtility.GoToMainStage() in ClosePrefabStage().

StageUtility.GoToMainStage() will block on a user prompt if the prefab stage has unsaved changes and Auto Save is disabled. In headless/automated environments (CI/CD, command-line tools), this hangs indefinitely with no user to respond.

Before switching stages, check prefabStage.hasUnsavedChanges and either save the stage (using EditorSceneManager.SaveScene(prefabStage.scene)) or explicitly discard changes (via the prefab stage API) to ensure the action completes deterministically without user interaction.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@MCPForUnity/Editor/Tools/ManageEditor.cs` around lines 372 - 385, Before
calling StageUtility.GoToMainStage() in ClosePrefabStage(), check the current
prefab stage for unsaved changes via PrefabStageUtility.GetCurrentPrefabStage()
and prefabStage.hasUnsavedChanges; if true, deterministically resolve them by
saving the scene with EditorSceneManager.SaveScene(prefabStage.scene) (and
handle/save failure) or explicitly discard changes via the prefab stage API
before calling StageUtility.GoToMainStage(); update ClosePrefabStage to perform
that check and only switch stages after saving or discarding to avoid blocking
in headless/CI environments.

Comment on lines +295 to +300
bool isRuntimeInstance = mat != null && !EditorUtility.IsPersistent(mat);
bool isNullWithPropertyBlock = mat == null && HasPropertyBlockColors(renderer, slot);
bool isNullMaterial = mat == null && !isNullWithPropertyBlock;

if (!isRuntimeInstance && !isNullWithPropertyBlock)
continue;
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Property-block colors on normal shared materials are still skipped.

The property-block path only runs when mat == null. A renderer that still has a persistent shared material plus a MaterialPropertyBlock color override falls through continue, so those tints still won't survive prefab serialization.

🎯 Suggested fix
-                    bool isRuntimeInstance = mat != null && !EditorUtility.IsPersistent(mat);
-                    bool isNullWithPropertyBlock = mat == null && HasPropertyBlockColors(renderer, slot);
+                    bool hasPropertyBlockColors = HasPropertyBlockColors(renderer, slot);
+                    bool isRuntimeInstance = mat != null && !EditorUtility.IsPersistent(mat);
+                    bool isPersistentMaterialWithPropertyBlock =
+                        mat != null && EditorUtility.IsPersistent(mat) && hasPropertyBlockColors;
+                    bool isNullWithPropertyBlock = mat == null && hasPropertyBlockColors;
                     bool isNullMaterial = mat == null && !isNullWithPropertyBlock;

-                    if (!isRuntimeInstance && !isNullWithPropertyBlock)
+                    if (!isRuntimeInstance && !isNullWithPropertyBlock && !isPersistentMaterialWithPropertyBlock)
                         continue;
📝 Committable suggestion

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

Suggested change
bool isRuntimeInstance = mat != null && !EditorUtility.IsPersistent(mat);
bool isNullWithPropertyBlock = mat == null && HasPropertyBlockColors(renderer, slot);
bool isNullMaterial = mat == null && !isNullWithPropertyBlock;
if (!isRuntimeInstance && !isNullWithPropertyBlock)
continue;
bool hasPropertyBlockColors = HasPropertyBlockColors(renderer, slot);
bool isRuntimeInstance = mat != null && !EditorUtility.IsPersistent(mat);
bool isPersistentMaterialWithPropertyBlock =
mat != null && EditorUtility.IsPersistent(mat) && hasPropertyBlockColors;
bool isNullWithPropertyBlock = mat == null && hasPropertyBlockColors;
bool isNullMaterial = mat == null && !isNullWithPropertyBlock;
if (!isRuntimeInstance && !isNullWithPropertyBlock && !isPersistentMaterialWithPropertyBlock)
continue;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@MCPForUnity/Editor/Tools/Prefabs/ManagePrefabs.cs` around lines 295 - 300,
The current check uses isNullWithPropertyBlock (which is true only when mat ==
null) so renderers with a persistent shared material plus a
MaterialPropertyBlock color override are skipped; change the logic to detect
property-block colors regardless of mat: call HasPropertyBlockColors(renderer,
slot) into a boolean like hasPropertyBlock and replace the continue condition
with if (!isRuntimeInstance && !hasPropertyBlock) continue so property-block
tints on non-null shared materials are handled.

Comment on lines +302 to +347
// Derive a unique asset path from the GameObject name and slot
string goName = renderer.gameObject.name.Replace(" ", "_");
string suffix = slot > 0 ? $"_slot{slot}" : "";
string matPath = $"{materialsFolder}/{goName}{suffix}_mat.mat";
matPath = AssetPathUtility.SanitizeAssetPath(matPath);
if (matPath == null)
{
McpLog.Warn($"[ManagePrefabs] Could not build safe material path for '{renderer.gameObject.name}', skipping.");
continue;
}

// Ensure the Materials directory exists
if (!AssetDatabase.IsValidFolder(materialsFolder))
{
string parentDir = Path.GetDirectoryName(materialsFolder).Replace("\\", "/");
string folderName = Path.GetFileName(materialsFolder);
AssetDatabase.CreateFolder(parentDir, folderName);
}

Material persisted = AssetDatabase.LoadAssetAtPath<Material>(matPath);
if (persisted == null)
{
// Create a new material with the correct shader for the active pipeline
Shader shader = isRuntimeInstance && mat.shader != null
? mat.shader
: RenderPipelineUtility.ResolveShader("Standard");
persisted = new Material(shader);
AssetDatabase.CreateAsset(persisted, matPath);
}

// Copy properties from the runtime instance if available
if (isRuntimeInstance)
{
persisted.CopyPropertiesFromMaterial(mat);
EditorUtility.SetDirty(persisted);
}
else if (isNullWithPropertyBlock)
{
// Extract color from the property block and apply to the new material
ApplyPropertyBlockToMaterial(renderer, slot, persisted);
EditorUtility.SetDirty(persisted);
}

sharedMats[slot] = persisted;
changed = true;
persistedPaths.Add(matPath);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Renderer-name collisions will merge distinct persisted materials.

matPath is based only on renderer.gameObject.name and slot. Two different renderers named Cube in separate branches resolve to the same asset path, so the later one reuses/overwrites the earlier .mat and both prefab children end up sharing the same persisted material.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@MCPForUnity/Editor/Tools/Prefabs/ManagePrefabs.cs` around lines 302 - 347,
matPath currently uses only renderer.gameObject.name and slot which causes
different renderers with the same name to collide; change the matPath generation
in ManagePrefabs so it includes a stable unique identifier for the prefab
instance (e.g. the prefab asset path or prefab GUID or a sanitized hierarchy
path) along with renderer.gameObject.name and slot before calling
AssetPathUtility.SanitizeAssetPath. Locate the matPath build around the
variables goName, suffix and materialsFolder, obtain a prefab identifier via
PrefabUtility.GetPrefabAssetPathOfNearestInstanceRoot(renderer.gameObject) or
PrefabUtility.GetPrefabInstanceHandle/GetLocalIdentifierInFile and incorporate a
short hash or sanitized version of that identifier into the filename (or folder)
to ensure distinct persisted .mat files for different prefabs while still using
AssetPathUtility.SanitizeAssetPath on the final path.

Comment on lines +355 to +359
// Clear any property blocks now that the material is persisted
for (int slot = 0; slot < sharedMats.Length; slot++)
{
renderer.SetPropertyBlock(null, slot);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Don't clear every MaterialPropertyBlock after migrating one slot.

HasPropertyBlockColors() currently returns !block.isEmpty, but ApplyPropertyBlockToMaterial() only copies _BaseColor and _Color. After that, Lines 355-359 clear property blocks for every slot on the renderer, which drops unrelated float/vector/texture overrides and can wipe untouched slots.

🧹 Narrow the detection first
         private static bool HasPropertyBlockColors(Renderer renderer, int slot)
         {
             MaterialPropertyBlock block = new MaterialPropertyBlock();
             renderer.GetPropertyBlock(block, slot);
-            return !block.isEmpty;
+            return block.HasColor("_BaseColor") || block.HasColor("_Color");
         }

Also applies to: 376-380

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@MCPForUnity/Editor/Tools/Prefabs/ManagePrefabs.cs` around lines 355 - 359,
HasPropertyBlockColors() and ApplyPropertyBlockToMaterial() currently treat any
non-empty MaterialPropertyBlock as a color candidate and then the code clears
all slots on the renderer (renderer.SetPropertyBlock(null, slot)), which wipes
unrelated overrides; narrow detection to only consider color properties and only
clear the specific slot after migration. Update HasPropertyBlockColors() to
check for presence of _BaseColor or _Color (use block.TryGetColor or
block.HasProperty for those names) instead of !block.isEmpty, change
ApplyPropertyBlockToMaterial() to remove/ignore only the color properties from
the block and then call renderer.SetPropertyBlock(null, slot) only for that same
slot when the block no longer contains any other properties; apply the same
per-slot-only logic to the similar block around the second occurrence (the
376-380 area) and stop clearing every slot in sharedMats.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (4)
unity-mcp-skill/references/workflows.md (1)

119-120: ⚠️ Potential issue | 🟠 Major

Correct the editor_state field path for compilation status.

The wait instruction references is_compiling at the root level, but the actual editor_state resource structure is compilation.is_compiling (or compilation.n in the serialized form). This mismatch will break wait loops.

Based on the editor_state.py implementation (Server/src/services/resources/editor_state.py), the correct path is editor_state["compilation"]["is_compiling"].

📝 Suggested fix
-# Read mcpforunity://editor/state → wait until is_compiling == false
+# Read mcpforunity://editor/state → wait until compilation.is_compiling == false

Also applies to: 235-235, 291-292, 520-521

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@unity-mcp-skill/references/workflows.md` around lines 119 - 120, The wait
logic reads the compilation flag from the wrong path; update all checks that
reference is_compiling at the root to use the nested path
editor_state["compilation"]["is_compiling"] (or its serialized form
compilation.n) so the wait loops check the actual compilation status; search for
occurrences of is_compiling in the workflow steps (e.g., the read of
mcpforunity://editor/state) and replace them with the nested
editor_state["compilation"]["is_compiling"] access.
.claude/skills/unity-mcp-skill/SKILL.md (2)

40-40: ⚠️ Potential issue | 🟠 Major

Correct the editor_state field path for compilation status.

Same issue as in workflows.md: the wait instruction references is_compiling at the root level, but the actual structure is editor_state["compilation"]["is_compiling"].

📝 Suggested fix
-# Read mcpforunity://editor/state → wait until is_compiling == false
+# Read mcpforunity://editor/state → wait until compilation.is_compiling == false

Also applies to: 198-198

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.claude/skills/unity-mcp-skill/SKILL.md at line 40, The wait instruction
currently reads mcpforunity://editor/state and checks editor_state.is_compiling
at the root; change the condition to reference the nested compilation flag
editor_state["compilation"]["is_compiling"] (or
editor_state.compilation.is_compiling) so it waits until that value is false;
update the same incorrect occurrence elsewhere in the file (the duplicate at the
second location) to use the nested path as well.

177-177: ⚠️ Potential issue | 🔴 Critical

Critical: Prefab instantiation API is documented incorrectly.

Same issue as in workflows.md: this line claims manage_gameobject(action="create", prefab_path="...") instantiates prefabs, but the prefab_path parameter is only used when save_as_prefab=True to specify where to save a newly created GameObject as a prefab file.

The documented API for prefab instantiation does not exist. Users following this guidance will encounter failures.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.claude/skills/unity-mcp-skill/SKILL.md at line 177, The docs incorrectly
state that manage_gameobject(action="create", prefab_path="...") instantiates
prefabs; update the SKILL.md entry to clarify that the prefab_path parameter is
only used when save_as_prefab=True to specify where to save a newly created
GameObject as a prefab file and that there is no current API to instantiate
prefabs via manage_gameobject; remove the false instantiation example/claim, and
optionally add a short note suggesting filing an enhancement if
prefab-instantiation functionality is required.
.claude/skills/unity-mcp-skill/references/workflows.md (1)

119-120: ⚠️ Potential issue | 🟠 Major

Same issue as in unity-mcp-skill/references/workflows.md.

This file appears to be a duplicate. The is_compiling field path issue flagged in the other workflows.md file also applies here.

Also applies to: 235-235, 291-292, 520-521, 422-450 (prefab instantiation)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.claude/skills/unity-mcp-skill/references/workflows.md around lines 119 -
120, This file is a duplicate and repeats the same incorrect field path and
prefab-instantiation logic found in the other workflows.md; remove or
consolidate this duplicate and update the references so they use the correct
state field path "is_compiling" from the canonical workflow (ensure reads from
mcpforunity://editor/state reference the exact field name used elsewhere), and
review the prefab instantiation steps (lines noted around the "prefab
instantiation" block) to match the corrected implementation in the primary
workflows.md; specifically fix the "manage_script update auto-triggers import +
compile" step to wait on the correct is_compiling boolean and align prefab
creation steps with the canonical sequence to avoid duplication and mismatched
field names.
🧹 Nitpick comments (2)
MCPForUnity/Editor/Tools/Prefabs/ManagePrefabs.cs (1)

371-385: Duplicate helper: consider extracting to shared utility.

This EnsureAssetFolderExists implementation is identical to the one in ManageMaterial.cs (lines 370-384). Consider extracting to a shared utility class like AssetPathUtility to eliminate duplication.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@MCPForUnity/Editor/Tools/Prefabs/ManagePrefabs.cs` around lines 371 - 385,
The EnsureAssetFolderExists implementation is duplicated (also present in
ManageMaterial.cs); extract it into a shared static utility (e.g., class
AssetPathUtility with a public static EnsureAssetFolderExists or
EnsureFolderExists method) and replace the local copies in ManagePrefabs.cs and
ManageMaterial.cs to call that utility; update both files to remove the
duplicated private method and reference the new shared method to centralize the
folder-creation logic.
MCPForUnity/Editor/Tools/ManageMaterial.cs (1)

370-384: Consider extracting shared helper to avoid duplication.

This EnsureAssetFolderExists implementation is duplicated in ManagePrefabs.cs (lines 371-385). Consider extracting this to a shared utility class (e.g., AssetPathUtility) to maintain DRY principles.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@MCPForUnity/Editor/Tools/ManageMaterial.cs` around lines 370 - 384, Duplicate
EnsureAssetFolderExists implementation found in ManageMaterial.cs and
ManagePrefabs.cs; extract it to a single shared static helper to remove
duplication. Create a new static class (e.g., AssetPathUtility) with a
public/static method EnsureAssetFolderExists(string assetFolderPath) containing
the existing folder-creation logic, keep method name the same to minimize
call-site changes, and replace the local implementations in both
ManageMaterial.cs (EnsureAssetFolderExists) and ManagePrefabs.cs with calls to
AssetPathUtility.EnsureAssetFolderExists(assetFolderPath); ensure the helper
lives in the same Editor namespace (or a common namespace) and adjust using
statements if needed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@MCPForUnity/Editor/Helpers/ComponentOps.cs`:
- Around line 544-554: The code currently accepts JTokenType.Float as valid
integer input and falls back to long.TryParse on the token's ToString(),
allowing values like 1.2 to be coerced into integers; update the validation in
the method where this block lives to explicitly reject JTokenType.Float (i.e.,
treat floats as invalid for integer properties) so the branch that sets
prop.longValue or prop.intValue only runs for integral tokens or parseable
integer strings; keep the error = "Expected integer value." and return false for
float tokens and avoid relying on ToString() coercion, referencing the existing
symbols prop.type, prop.longValue, prop.intValue, and
ParamCoercion.CoerceLong/CoerceInt to locate and change the conditional.

In `@MCPForUnity/Editor/Tools/Prefabs/ManagePrefabs.cs`:
- Around line 368-371: The XML doc summary above EnsureAssetFolderExists is
incorrect (it describes checking a renderer's MaterialPropertyBlock); update the
XML documentation for the method EnsureAssetFolderExists to accurately describe
its behavior (creating or ensuring an asset folder exists at the given path,
creating intermediate folders as needed) and adjust params/returns remarks to
match the method signature and behavior so the documentation reflects the actual
purpose of EnsureAssetFolderExists.

In `@unity-mcp-skill/references/workflows.md`:
- Around line 422-450: The docs wrongly claim manage_gameobject(action="create",
prefab_path="...") will instantiate prefabs; fix this by either (A) updating
workflows.md to remove/replace those examples with the correct current flow
(explain that prefab_path in manage_gameobject.py is only used when
action=="create" AND save_as_prefab=True and that manage_prefabs has no
instantiate action), or (B) implement proper instantiation support: add an
"instantiate" or "create_from_prefab" action to manage_prefabs (or extend
manage_gameobject to handle prefab_path for create without save_as_prefab) and
update the docs accordingly; reference manage_gameobject.py (prefab_path and
save_as_prefab logic) and the manage_prefabs actions (create_from_gameobject,
get_info, get_hierarchy, modify_contents) so the change is made in the correct
place.

---

Duplicate comments:
In @.claude/skills/unity-mcp-skill/references/workflows.md:
- Around line 119-120: This file is a duplicate and repeats the same incorrect
field path and prefab-instantiation logic found in the other workflows.md;
remove or consolidate this duplicate and update the references so they use the
correct state field path "is_compiling" from the canonical workflow (ensure
reads from mcpforunity://editor/state reference the exact field name used
elsewhere), and review the prefab instantiation steps (lines noted around the
"prefab instantiation" block) to match the corrected implementation in the
primary workflows.md; specifically fix the "manage_script update auto-triggers
import + compile" step to wait on the correct is_compiling boolean and align
prefab creation steps with the canonical sequence to avoid duplication and
mismatched field names.

In @.claude/skills/unity-mcp-skill/SKILL.md:
- Line 40: The wait instruction currently reads mcpforunity://editor/state and
checks editor_state.is_compiling at the root; change the condition to reference
the nested compilation flag editor_state["compilation"]["is_compiling"] (or
editor_state.compilation.is_compiling) so it waits until that value is false;
update the same incorrect occurrence elsewhere in the file (the duplicate at the
second location) to use the nested path as well.
- Line 177: The docs incorrectly state that manage_gameobject(action="create",
prefab_path="...") instantiates prefabs; update the SKILL.md entry to clarify
that the prefab_path parameter is only used when save_as_prefab=True to specify
where to save a newly created GameObject as a prefab file and that there is no
current API to instantiate prefabs via manage_gameobject; remove the false
instantiation example/claim, and optionally add a short note suggesting filing
an enhancement if prefab-instantiation functionality is required.

In `@unity-mcp-skill/references/workflows.md`:
- Around line 119-120: The wait logic reads the compilation flag from the wrong
path; update all checks that reference is_compiling at the root to use the
nested path editor_state["compilation"]["is_compiling"] (or its serialized form
compilation.n) so the wait loops check the actual compilation status; search for
occurrences of is_compiling in the workflow steps (e.g., the read of
mcpforunity://editor/state) and replace them with the nested
editor_state["compilation"]["is_compiling"] access.

---

Nitpick comments:
In `@MCPForUnity/Editor/Tools/ManageMaterial.cs`:
- Around line 370-384: Duplicate EnsureAssetFolderExists implementation found in
ManageMaterial.cs and ManagePrefabs.cs; extract it to a single shared static
helper to remove duplication. Create a new static class (e.g., AssetPathUtility)
with a public/static method EnsureAssetFolderExists(string assetFolderPath)
containing the existing folder-creation logic, keep method name the same to
minimize call-site changes, and replace the local implementations in both
ManageMaterial.cs (EnsureAssetFolderExists) and ManagePrefabs.cs with calls to
AssetPathUtility.EnsureAssetFolderExists(assetFolderPath); ensure the helper
lives in the same Editor namespace (or a common namespace) and adjust using
statements if needed.

In `@MCPForUnity/Editor/Tools/Prefabs/ManagePrefabs.cs`:
- Around line 371-385: The EnsureAssetFolderExists implementation is duplicated
(also present in ManageMaterial.cs); extract it into a shared static utility
(e.g., class AssetPathUtility with a public static EnsureAssetFolderExists or
EnsureFolderExists method) and replace the local copies in ManagePrefabs.cs and
ManageMaterial.cs to call that utility; update both files to remove the
duplicated private method and reference the new shared method to centralize the
folder-creation logic.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a00f9adf-cced-416c-b70c-736362ca1a84

📥 Commits

Reviewing files that changed from the base of the PR and between 0b56f0a and c304054.

📒 Files selected for processing (12)
  • .claude/skills/unity-mcp-skill/SKILL.md
  • .claude/skills/unity-mcp-skill/references/workflows.md
  • MCPForUnity/Editor/Helpers/ComponentOps.cs
  • MCPForUnity/Editor/Tools/ManageMaterial.cs
  • MCPForUnity/Editor/Tools/ManageScriptableObject.cs
  • MCPForUnity/Editor/Tools/Prefabs/ManagePrefabs.cs
  • MCPForUnity/Editor/Windows/Components/Advanced/McpAdvancedSection.uxml
  • Server/src/cli/commands/material.py
  • Server/src/services/tools/manage_editor.py
  • Server/src/services/tools/manage_material.py
  • unity-mcp-skill/SKILL.md
  • unity-mcp-skill/references/workflows.md
✅ Files skipped from review due to trivial changes (1)
  • Server/src/services/tools/manage_material.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • unity-mcp-skill/SKILL.md
  • MCPForUnity/Editor/Tools/ManageScriptableObject.cs

Comment on lines +544 to +554
if (value == null || value.Type == JTokenType.Null
|| (value.Type != JTokenType.Integer && value.Type != JTokenType.Float
&& !long.TryParse(value.ToString(), out _)))
{
if (value == null || value.Type == JTokenType.Null ||
(value.Type == JTokenType.String && !int.TryParse(value.ToString(), out _)))
{
error = "Expected integer value.";
return false;
}
error = "Expected integer value.";
return false;
}
prop.intValue = intVal;
if (prop.type == "long")
prop.longValue = ParamCoercion.CoerceLong(value, 0);
else
prop.intValue = ParamCoercion.CoerceInt(value, 0);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Reject non-integral floats for integer properties.

At Line 545-Line 546, any float is treated as valid integer input. Values like 1.2 can be silently coerced into an integer and overwrite serialized state unexpectedly.

🔧 Suggested fix
+using System.Globalization;
...
                     case SerializedPropertyType.Integer:
-                        if (value == null || value.Type == JTokenType.Null
-                            || (value.Type != JTokenType.Integer && value.Type != JTokenType.Float
-                                && !long.TryParse(value.ToString(), out _)))
+                        if (value == null || value.Type == JTokenType.Null)
                         {
                             error = "Expected integer value.";
                             return false;
                         }
+                        if (value.Type == JTokenType.Float)
+                        {
+                            double d = value.Value<double>();
+                            if (Math.Abs(d % 1d) > double.Epsilon)
+                            {
+                                error = "Expected integer value.";
+                                return false;
+                            }
+                        }
+                        else if (value.Type != JTokenType.Integer
+                                 && !long.TryParse(value.ToString(), NumberStyles.Integer, CultureInfo.InvariantCulture, out _))
+                        {
+                            error = "Expected integer value.";
+                            return false;
+                        }
                         if (prop.type == "long")
                             prop.longValue = ParamCoercion.CoerceLong(value, 0);
                         else
                             prop.intValue = ParamCoercion.CoerceInt(value, 0);
                         return true;
📝 Committable suggestion

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

Suggested change
if (value == null || value.Type == JTokenType.Null
|| (value.Type != JTokenType.Integer && value.Type != JTokenType.Float
&& !long.TryParse(value.ToString(), out _)))
{
if (value == null || value.Type == JTokenType.Null ||
(value.Type == JTokenType.String && !int.TryParse(value.ToString(), out _)))
{
error = "Expected integer value.";
return false;
}
error = "Expected integer value.";
return false;
}
prop.intValue = intVal;
if (prop.type == "long")
prop.longValue = ParamCoercion.CoerceLong(value, 0);
else
prop.intValue = ParamCoercion.CoerceInt(value, 0);
if (value == null || value.Type == JTokenType.Null)
{
error = "Expected integer value.";
return false;
}
if (value.Type == JTokenType.Float)
{
double d = value.Value<double>();
if (Math.Abs(d % 1d) > double.Epsilon)
{
error = "Expected integer value.";
return false;
}
}
else if (value.Type != JTokenType.Integer
&& !long.TryParse(value.ToString(), NumberStyles.Integer, CultureInfo.InvariantCulture, out _))
{
error = "Expected integer value.";
return false;
}
if (prop.type == "long")
prop.longValue = ParamCoercion.CoerceLong(value, 0);
else
prop.intValue = ParamCoercion.CoerceInt(value, 0);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@MCPForUnity/Editor/Helpers/ComponentOps.cs` around lines 544 - 554, The code
currently accepts JTokenType.Float as valid integer input and falls back to
long.TryParse on the token's ToString(), allowing values like 1.2 to be coerced
into integers; update the validation in the method where this block lives to
explicitly reject JTokenType.Float (i.e., treat floats as invalid for integer
properties) so the branch that sets prop.longValue or prop.intValue only runs
for integral tokens or parseable integer strings; keep the error = "Expected
integer value." and return false for float tokens and avoid relying on
ToString() coercion, referencing the existing symbols prop.type, prop.longValue,
prop.intValue, and ParamCoercion.CoerceLong/CoerceInt to locate and change the
conditional.

Comment on lines +422 to +450
### Instantiate Prefab in Scene

Use `manage_gameobject` (not `manage_prefabs`) to place prefab instances in the scene.

```python
# Full path
manage_gameobject(
action="create",
name="Enemy_1",
prefab_path="Assets/Prefabs/Enemy.prefab",
position=[5, 0, 3],
parent="Enemies"
)

# Smart lookup — just the prefab name works too
manage_gameobject(action="create", name="Enemy_2", prefab_path="Enemy", position=[10, 0, 3])

# Batch-spawn multiple instances
batch_execute(commands=[
{"tool": "manage_gameobject", "params": {
"action": "create", "name": f"Enemy_{i}",
"prefab_path": "Enemy", "position": [i * 3, 0, 0], "parent": "Enemies"
}}
for i in range(5)
])
```

> **Note:** `manage_prefabs` is for headless prefab editing (inspect, modify contents, create from GameObject). To *instantiate* a prefab into the scene, always use `manage_gameobject(action="create", prefab_path="...")`.

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify manage_gameobject does not support prefab instantiation via prefab_path
# Expected: prefab_path is only used with save_as_prefab, not for instantiation

echo "=== Checking manage_gameobject prefab_path usage ==="
rg -n -C5 'prefab_path' Server/src/services/tools/manage_gameobject.py

echo -e "\n=== Checking manage_prefabs available actions ==="
rg -n 'Literal\[' Server/src/services/tools/manage_prefabs.py | head -20

Repository: CoplayDev/unity-mcp

Length of output: 2310


🏁 Script executed:

#!/bin/bash
# Get full context around prefab_path usage in manage_gameobject
echo "=== Full manage_gameobject create action logic ==="
sed -n '160,230p' Server/src/services/tools/manage_gameobject.py

echo -e "\n=== Check if prefab_path is used outside save_as_prefab condition ==="
rg -n -B10 -A10 'prefabPath' Server/src/services/tools/manage_gameobject.py | head -60

Repository: CoplayDev/unity-mcp

Length of output: 6209


🏁 Script executed:

#!/bin/bash
# Search for how to actually instantiate prefabs in the codebase
echo "=== Search for instantiate related code ==="
rg -i 'instantiate' Server/src/ --type py -n

echo -e "\n=== Check C# implementation for prefab handling ==="
rg -i 'prefab' Assets/Scripts/ --type cs -l 2>/dev/null | head -5

Repository: CoplayDev/unity-mcp

Length of output: 159


🏁 Script executed:

#!/bin/bash
# Get manage_prefabs full implementation to see available actions
echo "=== manage_prefabs full implementation ==="
cat Server/src/services/tools/manage_prefabs.py

echo -e "\n=== Check manage_prefabs function signature ==="
rg -n 'async def manage_prefabs' Server/src/services/tools/manage_prefabs.py -A 30

Repository: CoplayDev/unity-mcp

Length of output: 14163


🏁 Script executed:

#!/bin/bash
# Search for C# prefab/instantiation code more broadly
echo "=== Search Assets for instantiate or prefab loading ==="
find Assets -name "*.cs" -type f -exec grep -l -i "instantiate\|prefab" {} \; 2>/dev/null | head -10

Repository: CoplayDev/unity-mcp

Length of output: 118


Critical: Prefab instantiation method documented does not exist in the API.

The section at lines 422-450 documents using manage_gameobject(action="create", prefab_path="...") to instantiate prefabs into the scene, but this functionality does not exist.

In manage_gameobject.py, the prefab_path parameter is only processed when both action == "create" AND save_as_prefab=True (line 203). It is used to specify where to save a newly created GameObject as a prefab file—not to load or instantiate from an existing prefab.

Additionally, manage_prefabs has no instantiation capability. Its only available actions are: create_from_gameobject, get_info, get_hierarchy, and modify_contents—none of which instantiate prefabs into a scene.

Impact: Users following this documentation will encounter failures. There is currently no documented way to instantiate prefabs into a scene using these tools.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@unity-mcp-skill/references/workflows.md` around lines 422 - 450, The docs
wrongly claim manage_gameobject(action="create", prefab_path="...") will
instantiate prefabs; fix this by either (A) updating workflows.md to
remove/replace those examples with the correct current flow (explain that
prefab_path in manage_gameobject.py is only used when action=="create" AND
save_as_prefab=True and that manage_prefabs has no instantiate action), or (B)
implement proper instantiation support: add an "instantiate" or
"create_from_prefab" action to manage_prefabs (or extend manage_gameobject to
handle prefab_path for create without save_as_prefab) and update the docs
accordingly; reference manage_gameobject.py (prefab_path and save_as_prefab
logic) and the manage_prefabs actions (create_from_gameobject, get_info,
get_hierarchy, modify_contents) so the change is made in the correct place.

@Scriptwonder Scriptwonder merged commit 3317eb1 into CoplayDev:beta Mar 15, 2026
1 of 2 checks passed
@Scriptwonder Scriptwonder deleted the bug-fix-and-doc-update-0315 branch March 15, 2026 21:40
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