Prefab Management Improvements: Pagination, Batch Operations, and Property Setting#666
Prefab Management Improvements: Pagination, Batch Operations, and Property Setting#666waqasrana wants to merge 10 commits intoCoplayDev:betafrom
Conversation
…bject When using manage_prefabs with create_child containing a prefab_path, the code was ignoring the prefab_path and creating an empty GameObject instead of a proper nested prefab instance. Now uses PrefabUtility.InstantiatePrefab() to create nested prefab instances that maintain their link to the source prefab. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Allows wiring serialized field references on components within prefabs. Supports referencing GameObjects or specific Components using path syntax (e.g., 'Canvas/Button:Button'). Uses reflection to set public fields, [SerializeField] private fields, and writable properties. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adds page_size, cursor, max_depth, and filter parameters to prevent excessive payload sizes on large prefabs. Response now includes pagination metadata (cursor, next_cursor, truncated). Preserves backward compatibility by keeping original BuildHierarchyItems method as a wrapper. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
… assets Enables setting component property values on objects within prefab assets, which was previously only possible on scene GameObjects via manage_components. Supports single property (property+value) and multiple properties (properties object) modes. Uses ComponentOps.SetProperty for consistent value conversion. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Response now includes createdChildren array with details about each created object: name, path, instanceId, transformInstanceId, isPrefabInstance, sourcePrefabGuid, sourcePrefabPath, and component info. Uses backward-compatible overloads with out parameters to preserve existing method signatures while adding new functionality. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Updated docstring to explicitly document that target accepts:
- Object name ('MyObject')
- Relative path ('Child/GrandChild')
- Full path with root ('RootName/Child/GrandChild')
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…on workflows Adds batch_modify action that loads a prefab once, applies multiple operations to different targets, then saves once. Each operation can specify its own target (defaults to prefab root) and modification parameters (position, rotation, scale, name, tag, layer, set_active, parent, components, create_child, set_property). Returns detailed operationResults array with per-operation success/failure info. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…lbacks ApplyModificationsToPrefabObject now accepts both camelCase and snake_case for setActive, componentsToAdd, and componentsToRemove parameters. This fixes batch_modify operations where Python passes snake_case keys directly. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When no page_size or cursor is provided, returns all items with the original response format. Pagination only applies when explicitly requested. Updated parameter description to reflect this behavior. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Tests added for: - batch_modify action (5 tests) - get_hierarchy pagination with page_size, cursor, max_depth, filter (4 tests) - set_property parameter for component property values (2 tests) - set_component_reference parameter (1 test) - create_child with prefab_path for nested prefab instantiation (1 test) - createdChildren response info (1 test) - snake_case parameter handling in batch_modify (1 test) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Reviewer's GuideExtends manage_prefabs with opt‑in, filterable pagination for get_hierarchy, adds batch_modify to apply multiple prefab edits per load/save, introduces set_property and set_component_reference operations, returns detailed metadata for created children (including nested prefab instances), fixes create_child with prefab_path, and wires all new capabilities through the Python manage_prefabs tool with snake_case/camelCase compatibility and comprehensive tests. Sequence diagram for batch_modify prefab operationssequenceDiagram
actor Client
participant PythonTool as manage_prefabs_py
participant Unity as ManagePrefabs_cs
participant PrefabUtil as PrefabUtility
participant AssetDb as AssetDatabase
Client->>PythonTool: manage_prefabs(action=batch_modify, prefab_path, operations[])
PythonTool->>PythonTool: Build params (prefabPath, operations)
PythonTool->>Unity: HandleCommand(params)
Unity->>Unity: Parse action=batch_modify
Unity->>Unity: BatchModifyContents(params)
Unity->>PrefabUtil: LoadPrefabContents(sanitizedPath)
PrefabUtil-->>Unity: prefabContents GameObject
loop For each operation in operations
Unity->>Unity: FindInPrefabContents(prefabContents, operation.target)
alt Target found
Unity->>Unity: ApplyModificationsToPrefabObject(targetGo, operation, prefabContents, out createdChildren)
alt create_child present
Unity->>Unity: CreateSingleChildInPrefab(..., out childInfo)
Unity->>Unity: Collect childInfo into createdChildren
end
alt set_component_reference present
Unity->>Unity: SetSingleComponentReference(...)
end
alt set_property present
Unity->>Unity: SetSingleComponentProperty(...)
end
Unity->>Unity: Accumulate modified flag and allCreatedChildren
else Target missing
Unity->>Unity: Record per-operation error
end
end
alt anyModified == true
Unity->>PrefabUtil: SaveAsPrefabAsset(prefabContents, sanitizedPath, out success)
PrefabUtil-->>Unity: success
Unity->>AssetDb: Refresh()
Unity->>Unity: McpLog.Info(batch summary)
Unity-->>PythonTool: SuccessResponse(modified=true, operationResults, createdChildren)
else
Unity-->>PythonTool: SuccessResponse(modified=false, operationResults, createdChildren)
end
Unity->>PrefabUtil: UnloadPrefabContents(prefabContents)
PythonTool-->>Client: JSON result with per-operation results and createdChildren
Sequence diagram for get_hierarchy with opt-in paginationsequenceDiagram
actor Client
participant PythonTool as manage_prefabs_py
participant Unity as ManagePrefabs_cs
participant PrefabUtil as PrefabUtility
Client->>PythonTool: manage_prefabs(action=get_hierarchy, prefab_path, page_size?, cursor?, max_depth?, filter?)
PythonTool->>PythonTool: Map args to params
PythonTool->>Unity: HandleCommand(params)
Unity->>Unity: Parse action=get_hierarchy
Unity->>Unity: GetHierarchy(params)
Unity->>Unity: Sanitize prefabPath
Unity->>PrefabUtil: LoadPrefabContents(sanitizedPath)
PrefabUtil-->>Unity: prefabContents GameObject
Unity->>Unity: Read pageSize, cursor, maxDepth, filter
Unity->>Unity: paginationRequested = pageSize or cursor provided
Unity->>Unity: BuildHierarchyItemsWithDepth(prefabContents.transform, sanitizedPath, maxDepth, filter)
Unity->>Unity: total = allItems.Count
alt paginationRequested == false
Unity-->>PythonTool: SuccessResponse(prefabPath, total, items = allItems)
else
Unity->>Unity: Clamp cursor and pageSize
Unity->>Unity: Compute end = min(total, cursor + pageSize)
Unity->>Unity: pagedItems = allItems[cursor:end]
Unity->>Unity: truncated = end < total
Unity->>Unity: next_cursor = truncated ? end.ToString() : null
Unity-->>PythonTool: SuccessResponse(prefabPath, total, cursor, pageSize, next_cursor, truncated, maxDepth, filter, items = pagedItems)
end
Unity->>PrefabUtil: UnloadPrefabContents(prefabContents)
PythonTool-->>Client: JSON hierarchy page (with or without pagination fields)
Class diagram for updated ManagePrefabs prefab management flowclassDiagram
class ManagePrefabs {
<<static>>
-ACTION_CREATE_FROM_GAMEOBJECT : string
-ACTION_GET_INFO : string
-ACTION_GET_HIERARCHY : string
-ACTION_MODIFY_CONTENTS : string
-ACTION_BATCH_MODIFY : string
-SupportedActions : string
+HandleCommand(params : JObject) object
-GetInfo(params : JObject) object
-GetHierarchy(params : JObject) object
-ModifyContents(params : JObject) object
-BatchModifyContents(params : JObject) object
-FindInPrefabContents(prefabContents : GameObject, target : string) GameObject
-ApplyModificationsToPrefabObject(targetGo : GameObject, params : JObject, prefabRoot : GameObject) (bool modified, ErrorResponse error)
-ApplyModificationsToPrefabObject(targetGo : GameObject, params : JObject, prefabRoot : GameObject, createdChildren : out List~object~) (bool modified, ErrorResponse error)
-CreateSingleChildInPrefab(createChildToken : JToken, defaultParent : GameObject, prefabRoot : GameObject) (bool created, ErrorResponse error)
-CreateSingleChildInPrefab(createChildToken : JToken, defaultParent : GameObject, prefabRoot : GameObject, createdInfo : out object) (bool created, ErrorResponse error)
-GetGameObjectPath(go : GameObject, root : GameObject) string
-GetComponentInfoList(go : GameObject) List~object~
-SetSingleComponentReference(refToken : JToken, targetGo : GameObject, prefabRoot : GameObject) (bool set, ErrorResponse error)
-SetSingleComponentProperty(propToken : JToken, targetGo : GameObject) (bool set, ErrorResponse error)
-BuildHierarchyItems(root : Transform, mainPrefabPath : string) List~object~
-BuildHierarchyItemsWithDepth(root : Transform, mainPrefabPath : string, maxDepth : int?, filter : string) List~object~
-BuildHierarchyItemsRecursiveWithDepth(transform : Transform, mainPrefabRoot : Transform, mainPrefabPath : string, parentPath : string, items : List~object~, currentDepth : int, maxDepth : int?, filterLower : string) void
}
class ComponentResolver {
+TryResolve(typeName : string, componentType : out Type, resolveError : out string) bool
+GetAllComponentProperties(componentType : Type) List~string~
}
class ComponentOps {
+SetProperty(targetComponent : Component, propertyName : string, valueToken : JToken, error : out string) bool
}
class PrefabUtilityHelper {
+GetComponentTypeNames(go : GameObject) List~string~
+GetPrefabNestingDepth(go : GameObject, mainPrefabRoot : Transform) int
+GetParentPrefabPath(go : GameObject, mainPrefabRoot : Transform) string
+GetNestedPrefabPath(go : GameObject) string
}
class AssetPathUtility {
+SanitizeAssetPath(path : string) string
}
class ErrorResponse {
+Error : string
}
class SuccessResponse {
+Message : string
+Data : object
}
class McpLog {
+Info(message : string) void
+Warn(message : string) void
}
class PrefabUtility {
+LoadPrefabContents(path : string) GameObject
+SaveAsPrefabAsset(prefabRoot : GameObject, path : string, success : out bool) void
+UnloadPrefabContents(prefabRoot : GameObject) void
+InstantiatePrefab(prefabAsset : Object, parent : Transform) Object
+IsAnyPrefabInstanceRoot(go : GameObject) bool
}
class AssetDatabase {
+LoadAssetAtPath~GameObject~(path : string) GameObject
+AssetPathToGUID(path : string) string
+Refresh() void
}
ManagePrefabs --> ComponentResolver : uses for type resolution
ManagePrefabs --> ComponentOps : uses for property setting
ManagePrefabs --> PrefabUtilityHelper : uses for hierarchy metadata
ManagePrefabs --> AssetPathUtility : uses for path sanitization
ManagePrefabs --> ErrorResponse : returns on failure
ManagePrefabs --> SuccessResponse : returns on success
ManagePrefabs --> McpLog : logs operations
ManagePrefabs --> PrefabUtility : loads/saves prefabs
ManagePrefabs --> AssetDatabase : loads source prefabs and GUIDs
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
📝 WalkthroughWalkthroughThis pull request introduces batch modification capabilities, pagination, filtering, and depth controls for prefab hierarchies. New features include a Changes
Sequence DiagramsequenceDiagram
participant Client
participant Server
participant ManagePrefabs as Unity<br/>ManagePrefabs
participant PrefabSystem as Prefab<br/>System
Client->>Server: batch_modify(prefab_path, operations[])
Server->>ManagePrefabs: BatchModifyContents(operations)
ManagePrefabs->>PrefabSystem: LoadPrefabContents(prefab_path)
loop For each operation
ManagePrefabs->>ManagePrefabs: ApplyModificationsToPrefabObject()
alt Operation type
ManagePrefabs->>ManagePrefabs: SetSingleComponentReference()
ManagePrefabs->>ManagePrefabs: SetSingleComponentProperty()
else Create Child
ManagePrefabs->>PrefabSystem: CreateSingleChildInPrefab()
PrefabSystem-->>ManagePrefabs: createdInfo (path, IDs, components)
end
ManagePrefabs->>ManagePrefabs: Collect per-operation result
end
ManagePrefabs->>PrefabSystem: SavePrefabAsset()
ManagePrefabs->>PrefabSystem: UnloadPrefabContents()
ManagePrefabs-->>Server: {results[], summary}
Server-->>Client: {success, created_children, per_op_results}
Estimated Code Review Effort🎯 4 (Complex) | ⏱️ ~60 minutes The review requires careful analysis of interrelated logic across multiple layers: batch operation sequencing and state tracking in ManagePrefabs.cs, parameter propagation and validation in the server API, and comprehensive test scenarios validating both success and error paths. The addition of reflection-based property/reference setting and pagination logic increases complexity, though the changes follow consistent patterns. Possibly Related PRs
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Hey - I've found 1 issue
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location> `MCPForUnity/Editor/Tools/Prefabs/ManagePrefabs.cs:1346-1355` </location>
<code_context>
+ UnityEngine.Object objectToAssign;
</code_context>
<issue_to_address>
**suggestion:** Handle GameObject fields when the reference_target explicitly specifies a Component type.
When `reference_target` includes a component suffix (e.g., `"Canvas/Button:Button"`), `objectToAssign` will always be a `Component`. For `GameObject`-typed fields/properties this fails the `IsAssignableFrom` check and returns a type mismatch, even though `component.gameObject` would be valid.
You already handle the reverse (field expects a `Component` but `objectToAssign` is a `GameObject`). Please mirror that for `GameObject` targets, e.g.:
```csharp
typeof(GameObject).IsAssignableFrom(fieldType) && objectToAssign is Component c
? c.gameObject
: objectToAssign;
```
and apply the same logic for properties, so `reference_target` behaves consistently regardless of whether the target member is a `Component` or `GameObject`.
Suggested implementation:
```csharp
// Determine what object to assign (GameObject or Component)
UnityEngine.Object objectToAssign;
```
```csharp
Component refComponent = referencedGo.GetComponent(refCompType);
if (refComponent == null)
{
```
```csharp
// Now assign the reference to the field
var fieldType = fieldInfo.FieldType;
UnityEngine.Object fieldObjectToAssign =
typeof(GameObject).IsAssignableFrom(fieldType) && objectToAssign is Component cForGo
? cForGo.gameObject
: objectToAssign;
if (!fieldType.IsAssignableFrom(fieldObjectToAssign.GetType()))
{
return (false, new ErrorResponse(
$"Type mismatch when assigning reference to field '{fieldInfo.Name}'. " +
$"Field type: {fieldType.FullName}, " +
$"Assigned object type: {fieldObjectToAssign.GetType().FullName}."));
}
fieldInfo.SetValue(targetComponent, fieldObjectToAssign);
```
```csharp
// Now assign the reference to the property
var propertyType = propertyInfo.PropertyType;
UnityEngine.Object propertyObjectToAssign =
typeof(GameObject).IsAssignableFrom(propertyType) && objectToAssign is Component cForGoProp
? cForGoProp.gameObject
: objectToAssign;
if (!propertyType.IsAssignableFrom(propertyObjectToAssign.GetType()))
{
return (false, new ErrorResponse(
$"Type mismatch when assigning reference to property '{propertyInfo.Name}'. " +
$"Property type: {propertyType.FullName}, " +
$"Assigned object type: {propertyObjectToAssign.GetType().FullName}."));
}
propertyInfo.SetValue(targetComponent, propertyObjectToAssign);
```
I assumed the existing assignment logic for fields and properties looks like the `IsAssignableFrom` checks in the SEARCH blocks above. If your actual code differs (e.g., variable names, structure, or error messages), adjust the SEARCH patterns accordingly and apply the same wrapping pattern:
1. Introduce a local `fieldObjectToAssign` (for fields) / `propertyObjectToAssign` (for properties).
2. Set it via:
`typeof(GameObject).IsAssignableFrom(expectedType) && objectToAssign is Component c ? c.gameObject : objectToAssign;`
3. Use that local both in the `IsAssignableFrom` check and in the `SetValue` call.
Make sure to keep any existing special handling you already have for `Component`-typed fields where `objectToAssign` is a `GameObject`; this new logic is additive and should mirror that behavior in the opposite direction.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
MCPForUnity/Editor/Tools/Prefabs/ManagePrefabs.cs (1)
755-1024:⚠️ Potential issue | 🔴 CriticalAdd required
using staticimport for ComponentResolver access.The code at lines 883, 902, 1177, 1317, 1350, 1394, and 1482 calls
ComponentResolver.TryResolve()andComponentResolver.GetAllComponentProperties()directly, but the file lacks the required import statement. Since ComponentResolver is a nested static class within ManageGameObject, add this to the top of ManagePrefabs.cs:using static MCPForUnity.Editor.Tools.ManageGameObject;Without this import, all ComponentResolver calls will fail to compile.
code compiles fine. |
Summary
This PR enhances
manage_prefabswith several new capabilities to improve AI-driven prefab workflows:get_hierarchy- Prevents excessive payloads (100k+ chars) on large prefabs withpage_size,cursor,max_depth, andfilterparameters. Opt-in tomaintain backward compatibility.
set_propertysupport - Set component property values directly on prefab assets (likemanage_components set_propertybut for prefabs)set_component_reference- Wire serialized field references to other objects within prefabsbatch_modifyaction - Apply multiple operations to different targets in a single prefab load/save cycle for efficiencycreatedChildrenresponse - Detailed feedback on created children including name, path, instanceId, isPrefabInstance, sourcePrefabPath, etc.create_childwithprefab_pathcreating empty GameObjects instead of nested prefab instancesBackward Compatibility
get_hierarchyreturns all items by default (original behavior); pagination only activates whenpage_sizeorcursoris explicitly providedoutparametersTest Plan
batch_modifywith multiple operations includingcomponents_to_add,create_child,set_propertynext_cursorvaluesCommits
🤖 Generated with Claude Code
Summary by Sourcery
Enhance Unity prefab management tooling with pagination and filtering for hierarchy queries, richer child creation metadata, batch modification support, and new component reference/property-setting capabilities, while updating the Python wrapper and adding extensive tests.
New Features:
Bug Fixes:
Enhancements:
Tests:
Summary by CodeRabbit