Skip to content

feat: add component reference wiring — get_referenceable, set_reference, batch_wire#1043

Open
zaferdace wants to merge 2 commits intoCoplayDev:mainfrom
zaferdace:feat/component-reference-wiring
Open

feat: add component reference wiring — get_referenceable, set_reference, batch_wire#1043
zaferdace wants to merge 2 commits intoCoplayDev:mainfrom
zaferdace:feat/component-reference-wiring

Conversation

@zaferdace
Copy link
Copy Markdown
Contributor

@zaferdace zaferdace commented Apr 6, 2026

Summary

Extends manage_components with 3 new actions for automating object reference field assignment — the programmatic equivalent of drag-and-drop in the Inspector.

New Actions

Action What it does
get_referenceable List scene objects and project assets assignable to a reference field
set_reference Assign an object reference to a component field
batch_wire Wire multiple references in one atomic call

Example Usage

# "What can I assign to the Player's AudioSource.clip field?"
manage_components(
    action="get_referenceable",
    target="Player",
    component_type="AudioSource",
    property="clip"
)

# "Set the Main Camera's target to the Player transform"
manage_components(
    action="set_reference",
    target="MainCamera",
    component_type="CinemachineCamera",
    property="Follow",
    reference_path="Player"
)

# "Wire up all references on the GameManager at once"
manage_components(
    action="batch_wire",
    target="GameManager",
    component_type="GameManager",
    references=[
        {"property_name": "player", "reference_path": "Player"},
        {"property_name": "scoreUI", "reference_path": "Canvas/ScoreText"},
        {"property_name": "bgMusic", "reference_asset_path": "Assets/Audio/BGM.mp3"},
    ]
)

Implementation Details

Python (Server/src/services/tools/manage_components.py):

  • Added new parameters: reference_path, reference_asset_path, reference_instance_id, references, clear, atomic, include_scene, include_assets, limit
  • Routes through send_with_unity_instance same as existing actions

C# (MCPForUnity/Editor/Tools/ManageComponents.cs):

  • get_referenceable: resolves field type via reflection, searches scene (FindObjectsOfType) and assets (AssetDatabase.FindAssets) for matching types
  • set_reference: uses SerializedObject + SerializedProperty.objectReferenceValue for correct prefab override tracking and Undo support
  • batch_wire: atomic mode (default) validates all entries first, then applies all writes in a single Undo group
  • Shared helpers: TryGetComponentAndObjectReferenceProperty, ResolveReference, ValidateReferenceAssignment

Design Decisions

  • Extends existing tool rather than new top-level tools — follows the manage_* multi-action pattern
  • SerializedProperty for writes (not raw reflection) — correct dirty state, prefab overrides, Undo
  • Undo support on all mutations via Undo.RecordObject + grouped operations for batch
  • Type validation before assignment — prevents assigning wrong types
  • Three reference selectors: scene path, asset path, instance ID — covers all common cases
  • Auto-component resolution: if field expects a Component type but a GameObject is provided, automatically calls GetComponent

Reference Selectors

Selector Use Case
reference_path Scene object by hierarchy path (e.g., "Player/Model")
reference_asset_path Project asset (e.g., "Assets/Materials/Red.mat")
reference_instance_id Direct instance ID (most precise)
clear=true Set reference to null

Test plan

  • get_referenceable returns correct types for Transform, AudioClip, Material fields
  • set_reference with scene object path
  • set_reference with project asset path
  • set_reference with instance ID
  • set_reference with clear=true
  • set_reference type mismatch returns error
  • batch_wire atomic success (all valid)
  • batch_wire atomic failure (one invalid, none applied)
  • batch_wire non-atomic partial success
  • Undo works after set_reference and batch_wire
  • Prefab override tracking works correctly

Summary by Sourcery

Extend the manage_components tool to support querying and assigning Unity object reference fields, including batch wiring of multiple references.

New Features:

  • Add get_referenceable action to list valid scene objects and assets for a given object reference property.
  • Add set_reference action to assign or clear a single object reference on a component property with proper Unity editor integration.
  • Add batch_wire action to validate and apply multiple reference assignments in one call, with optional atomic behavior.

Enhancements:

  • Enhance manage_components Python wrapper with parameters for reference paths, asset paths, instance IDs, batch reference lists, and query controls for referenceable targets.

…ce, batch_wire

Extends manage_components with 3 new actions for object reference fields:

- get_referenceable: list assignable scene objects and project assets for
  a component's reference field, with type filtering
- set_reference: assign an object reference via path, asset path, or
  instance ID, with SerializedProperty + Undo support
- batch_wire: wire multiple references atomically in one call, with
  validation-first approach and grouped Undo

C# uses SerializedObject/SerializedProperty for correct prefab override
tracking, dirty state, and Undo. Reference resolution supports scene
objects, project assets, and component auto-resolution from GameObjects.
@sourcery-ai
Copy link
Copy Markdown
Contributor

sourcery-ai bot commented Apr 6, 2026

Reviewer's Guide

Extends the existing manage_components tool with three new actions (get_referenceable, set_reference, batch_wire) to support type-safe Unity object reference discovery and assignment from both Python and the Unity editor, including batch atomic wiring, Undo/prefab override correctness, and flexible reference selectors (scene path, asset path, instance ID).

Sequence diagram for set_reference action in manage_components

sequenceDiagram
    actor PythonClient
    participant PythonTool as manage_components_py
    participant UnityBridge as send_with_unity_instance
    participant UnityEditor as ManageComponents_HandleCommand
    participant RefHelper as SetReference
    participant Validator as ValidateReferenceAssignment

    PythonClient->>PythonTool: manage_components(action=set_reference, target, component_type, property, reference_path/reference_asset_path/reference_instance_id/clear)
    PythonTool->>PythonTool: Build params dict
    PythonTool->>UnityBridge: send_with_unity_instance("manage_components", params)
    UnityBridge->>UnityEditor: HandleCommand(params)

    UnityEditor->>UnityEditor: Parse action and target
    UnityEditor->>RefHelper: SetReference(params, targetToken, searchMethod)

    RefHelper->>RefHelper: TryGetComponentAndObjectReferenceProperty
    RefHelper-->>UnityEditor: ErrorResponse? (if failure)
    RefHelper->>Validator: ValidateReferenceAssignment(component, propertyName, expectedType, params)
    Validator->>Validator: HasReferenceSelector / ResolveReference
    Validator->>Validator: ConvertResolvedObject
    Validator-->>RefHelper: ReferenceAssignmentValidation

    alt validation fails
        RefHelper-->>UnityEditor: ErrorResponse(validation.Error)
        UnityEditor-->>UnityBridge: error payload
        UnityBridge-->>PythonTool: error dict
        PythonTool-->>PythonClient: error result
    else validation succeeds
        RefHelper->>UnityEditor: Undo.RecordObject(component)
        RefHelper->>RefHelper: property.objectReferenceValue = ResolvedObject
        RefHelper->>RefHelper: serializedObject.ApplyModifiedProperties
        RefHelper->>UnityEditor: EditorUtility.SetDirty(component)
        RefHelper->>UnityEditor: MarkOwningSceneDirty(targetGo)
        RefHelper-->>UnityEditor: success payload with previous_value and new_value
        UnityEditor-->>UnityBridge: success payload
        UnityBridge-->>PythonTool: success dict
        PythonTool-->>PythonClient: success result
    end
Loading

Updated class diagram for ManageComponents reference wiring

classDiagram
    class ManageComponents {
        +HandleCommand(@params : JObject) object
        -GetReferenceable(@params : JObject, targetToken : JToken, searchMethod : string) object
        -SetReference(@params : JObject, targetToken : JToken, searchMethod : string) object
        -BatchWire(@params : JObject, targetToken : JToken, searchMethod : string) object
        -TryGetComponentAndObjectReferenceProperty(@params : JObject, targetToken : JToken, searchMethod : string, actionName : string, targetGo : GameObject, component : Component, serializedObject : SerializedObject, property : SerializedProperty, expectedType : Type, error : ErrorResponse) bool
        -GetFieldType(component : Component, propertyName : string) Type
        -ResolveReference(refParams : JObject) UnityEngine.Object
        -FindReferenceableSceneObjects(expectedType : Type, limit : int) IEnumerable~object~
        -FindReferenceableAssets(expectedType : Type, limit : int) IEnumerable~object~
        -BuildAssetSearchFilter(expectedType : Type) string
        -LoadReferenceableAssetsAtPath(assetPath : string, expectedType : Type) IEnumerable~UnityEngine.Object~
        -ValidateReferenceAssignment(component : Component, propertyName : string, expectedType : Type, refParams : JObject) ReferenceAssignmentValidation
        -HasReferenceSelector(refParams : JObject) bool
        -ConvertResolvedObject(resolved : UnityEngine.Object, expectedType : Type) UnityEngine.Object
        -DescribeObjectReference(obj : UnityEngine.Object) object
    }

    class ReferenceAssignmentValidation {
        -PropertyName : string
        -Success : bool
        -Error : string
        -ResolvedObject : UnityEngine.Object
        +Ok(propertyName : string, resolvedObject : UnityEngine.Object) ReferenceAssignmentValidation
        +Fail(propertyName : string, error : string) ReferenceAssignmentValidation
    }

    class BatchWireResult {
        +Property : string
        +Success : bool
        +Error : string
        +NewValue : object
        +ToResponse() object
    }

    class GameObjectLookup {
        +ResolveInstanceID(instanceId : int) UnityEngine.Object
        +FindByTarget(target : JToken, searchMethod : string, includeInactive : bool) GameObject
        +GetAllSceneObjects(includeInactive : bool) IEnumerable~GameObject~
        +GetGameObjectPath(gameObject : GameObject) string
    }

    class UnityTypeResolver {
        +ResolveComponent(componentType : string) Type
    }

    class ErrorResponse {
        +ErrorResponse(message : string)
        +message : string
    }

    class SerializedObject
    class SerializedProperty {
        +propertyType : SerializedPropertyType
        +propertyPath : string
        +objectReferenceValue : UnityEngine.Object
    }

    class UnityEngine.Object {
        +GetInstanceID() int
        +name : string
        +GetType() Type
    }

    class Component {
        +gameObject : GameObject
        +GetComponent(type : Type) Component
    }

    class GameObject {
        +name : string
        +GetComponent(type : Type) Component
    }

    class Undo {
        +RecordObject(target : UnityEngine.Object, name : string) void
        +GetCurrentGroup() int
        +SetCurrentGroupName(name : string) void
        +CollapseUndoOperations(group : int) void
    }

    class AssetDatabase {
        +FindAssets(filter : string) string[]
        +GUIDToAssetPath(guid : string) string
        +LoadAssetAtPath~T~(assetPath : string) T
        +LoadAllAssetsAtPath(assetPath : string) UnityEngine.Object[]
        +GetAssetPath(obj : UnityEngine.Object) string
    }

    class EditorUtility {
        +SetDirty(obj : UnityEngine.Object) void
    }

    class SerializedPropertyType {
    }

    ManageComponents --> ReferenceAssignmentValidation : uses
    ManageComponents --> BatchWireResult : uses
    ManageComponents --> GameObjectLookup : uses
    ManageComponents --> UnityTypeResolver : uses
    ManageComponents --> ErrorResponse : returns
    ManageComponents --> SerializedObject : uses
    ManageComponents --> SerializedProperty : uses
    ManageComponents --> Undo : uses
    ManageComponents --> AssetDatabase : uses
    ManageComponents --> EditorUtility : uses
    ManageComponents --> Component : manipulates
    ManageComponents --> GameObject : manipulates
    ReferenceAssignmentValidation --> UnityEngine.Object : holds
    BatchWireResult --> UnityEngine.Object : via NewValue description
Loading

File-Level Changes

Change Details Files
Add three new manage_components actions for object reference discovery and assignment in the Unity editor backend.
  • Extend the supported action list and error messages to include get_referenceable, set_reference, and batch_wire.
  • Implement GetReferenceable to validate an object reference property and return matching scene objects and assets with metadata, controlled by include_scene/include_assets/limit.
  • Implement SetReference to resolve a target via reference selectors, validate type compatibility, and write through SerializedProperty with Undo, dirtying, and scene-marking.
  • Implement BatchWire to resolve and optionally atomically apply multiple reference assignments to a single component, with per-entry validation, Undo grouping, and structured result reporting.
  • Introduce shared helper types and methods (TryGetComponentAndObjectReferenceProperty, GetFieldType, ResolveReference, DescribeObjectReference, validation/result structs) to support reference operations and type resolution.
MCPForUnity/Editor/Tools/ManageComponents.cs
Expose new reference wiring actions and parameters in the Python manage_components tool and route them to the Unity instance.
  • Expand the manage_components action literal and description to include get_referenceable, set_reference, and batch_wire with updated help text.
  • Add optional parameters for reference selectors (reference_path, reference_asset_path, reference_instance_id), batch wiring (references, atomic), and discovery controls (include_scene, include_assets, limit, clear).
  • Populate the outbound params object conditionally based on the chosen action so that Unity receives only the relevant fields for get_referenceable, set_reference, and batch_wire.
  • Update the missing-action error message to list the new supported actions.
Server/src/services/tools/manage_components.py

Possibly linked issues

  • #0: PR adds explicit reference wiring APIs to manage_components, addressing the issue’s request for reference-type field support.

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
Copy Markdown
Contributor

coderabbitai bot commented Apr 6, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c9d642f7-bea3-4688-9924-a4909431b6c1

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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
Copy Markdown
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:

  • In BatchWire, you recreate a new SerializedObject for the same component on every validation during apply; consider constructing it once and reusing it (or using the one from TryGetComponentAndObjectReferenceProperty-like helper) to avoid unnecessary allocations and simplify the write path.
  • GetFieldType currently only resolves backing fields and will return null for array/list element paths or more complex SerializedProperty paths; if you intend to support wiring into collection elements, you may want to extend this logic to derive the element type from the property path rather than simple field reflection.
  • There is some duplication between SetReference and the apply loop in BatchWire (Undo recording, SerializedObject/SerializedProperty handling, marking dirty); extracting a shared helper for "apply validated reference to property" would reduce repetition and make future changes to the write semantics less error-prone.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `BatchWire`, you recreate a new `SerializedObject` for the same component on every `validation` during apply; consider constructing it once and reusing it (or using the one from `TryGetComponentAndObjectReferenceProperty`-like helper) to avoid unnecessary allocations and simplify the write path.
- `GetFieldType` currently only resolves backing fields and will return null for array/list element paths or more complex `SerializedProperty` paths; if you intend to support wiring into collection elements, you may want to extend this logic to derive the element type from the property path rather than simple field reflection.
- There is some duplication between `SetReference` and the apply loop in `BatchWire` (Undo recording, `SerializedObject`/`SerializedProperty` handling, marking dirty); extracting a shared helper for "apply validated reference to property" would reduce repetition and make future changes to the write semantics less error-prone.

## Individual Comments

### Comment 1
<location path="MCPForUnity/Editor/Tools/ManageComponents.cs" line_range="719" />
<code_context>
+
+        private static UnityEngine.Object ResolveReference(JObject refParams)
+        {
+            var instanceId = refParams["reference_instance_id"]?.Value<int>();
+            if (instanceId.HasValue)
+                return GameObjectLookup.ResolveInstanceID(instanceId.Value);
</code_context>
<issue_to_address>
**issue (bug_risk):** Use safer coercion for `reference_instance_id` to avoid exceptions on malformed input.

`JToken.Value<int>()` will throw if `reference_instance_id` is present but not a valid int (e.g., string, float, null), and this input comes from an external tool. That means a malformed payload can cause an exception before you can surface a structured validation error. Prefer using `ParamCoercion.CoerceInt(refParams[
</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.

- Extract ApplyReferenceToProperty shared helper to deduplicate
  SetReference and BatchWire write logic
- BatchWire now reuses single SerializedObject instead of recreating
  per validation entry
- ResolveReference uses ParamCoercion.CoerceInt instead of unsafe
  JToken.Value<int>() to prevent exceptions on malformed input
- GetFieldType now handles array/list element paths
  (e.g., "targets.Array.data[0]") by extracting element type
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant