Overview
The ungroup_objects tool dissolves a Model or Folder container, moving all of its children to the container’s parent and then deleting the container. This is useful for flattening hierarchies and removing unnecessary organizational layers.
Use Cases
- Remove unnecessary organizational containers
- Flatten deeply nested hierarchies
- Extract objects from a group for individual manipulation
- Clean up after importing models with excessive grouping
- Simplify hierarchy structure
- Merge grouped objects into parent level
- Undo a previous group_objects operation
Parameters
Path to the Model or Folder to ungroup
Response Structure
Whether the ungroup operation succeeded
Number of children moved to the parent
Path to the parent where children were moved
Array of objects that were moved
New path after ungrouping
Example Response
{
"success": true,
"childrenMoved": 3,
"newParent": "game.Workspace",
"movedObjects": [
{
"name": "Platform_1",
"newPath": "game.Workspace.Platform_1"
},
{
"name": "Platform_2",
"newPath": "game.Workspace.Platform_2"
},
{
"name": "Platform_3",
"newPath": "game.Workspace.Platform_3"
}
]
}
Usage Examples
Basic Ungroup
{
"instancePath": "game.Workspace.GroupedObjects"
}
Dissolves the “GroupedObjects” Model/Folder, moving all children to Workspace.
Ungroup Model
// Ungroup a model to access individual parts
const result = await mcpClient.callTool('ungroup_objects', {
instancePath: 'game.Workspace.LevelModel'
});
console.log(`Ungrouped ${result.childrenMoved} objects from LevelModel`);
console.log('Objects moved to:', result.newParent);
Flatten Nested Folders
// Remove all organizational folders in a hierarchy
const folders = await mcpClient.callTool('get_descendants', {
instancePath: 'game.Workspace.ComplexHierarchy',
classFilter: 'Folder'
});
console.log(`Found ${folders.descendants.length} folders to ungroup`);
// Ungroup from deepest to shallowest to avoid path changes
for (const folder of folders.descendants.reverse()) {
const result = await mcpClient.callTool('ungroup_objects', {
instancePath: folder.path
});
console.log(`Ungrouped ${folder.name}: ${result.childrenMoved} children`);
}
console.log('Hierarchy flattened');
Selective Ungrouping
// Ungroup only specific Models
const models = await mcpClient.callTool('search_objects', {
query: 'Temp',
searchType: 'name'
});
for (const model of models.results) {
if (model.className === 'Model') {
await mcpClient.callTool('ungroup_objects', {
instancePath: model.path
});
console.log(`Ungrouped ${model.name}`);
}
}
Undo Grouping
// Undo an accidental grouping
const result = await mcpClient.callTool('ungroup_objects', {
instancePath: 'game.Workspace.AccidentalGroup'
});
if (result.success) {
console.log('Grouping undone successfully');
console.log(`${result.childrenMoved} objects restored to ${result.newParent}`);
}
Clean Up Imported Models
// Flatten unnecessarily complex imported model structure
const importedModel = 'game.Workspace.ImportedAsset';
// Get all sub-models
const subModels = await mcpClient.callTool('get_descendants', {
instancePath: importedModel,
classFilter: 'Model'
});
// Ungroup all sub-models
for (const subModel of subModels.descendants) {
await mcpClient.callTool('ungroup_objects', {
instancePath: subModel.path
});
}
console.log('Imported model flattened');
// Extract specific objects from a group, then delete the group
const group = 'game.Workspace.MixedGroup';
// Ungroup to parent level
const result = await mcpClient.callTool('ungroup_objects', {
instancePath: group
});
// Now objects are at parent level and can be manipulated individually
for (const obj of result.movedObjects) {
console.log(`Now accessible at: ${obj.newPath}`);
// Modify individual objects as needed
await mcpClient.callTool('set_property', {
instancePath: obj.newPath,
propertyName: 'Transparency',
propertyValue: 0.5
});
}
Batch Ungroup Selected Models
// Ungroup all selected Models
const selection = await mcpClient.callTool('get_selection', {});
const models = selection.selection.filter(obj =>
obj.className === 'Model' || obj.className === 'Folder'
);
if (models.length === 0) {
console.log('No Models or Folders selected');
} else {
for (const model of models) {
const result = await mcpClient.callTool('ungroup_objects', {
instancePath: model.path
});
console.log(`Ungrouped ${model.name}: ${result.childrenMoved} children`);
}
}
Tips and Best Practices
When ungrouping nested structures, work from deepest to shallowest levels to avoid path changes invalidating subsequent operations.
Ungrouping permanently deletes the container. If you need to keep the container, use mass_reparent to move children out instead.
Physical parts in a Model may rely on the Model’s properties (like PrimaryPart). After ungrouping, these relationships are lost.
If parts were welded or constrained within the Model, those constraints remain but may need adjustment after ungrouping.
Behavior Details
What Happens During Ungrouping
- All children of the group are moved to the group’s parent
- The empty group container is deleted
- Children maintain all their properties, attributes, and tags
- Descendants of children (grandchildren, etc.) remain with their parents
Position Behavior
- Parts maintain their world position when moved
- Since parts use world coordinates, visual position doesn’t change
- However, hierarchy-based scripts may break if they rely on the old parent
Model-Specific Behavior
- PrimaryPart reference is lost (only existed on the Model)
- Model-level properties (like ModelStreamingMode) are lost
- Welds and constraints between parts are preserved
Folder-Specific Behavior
- Folders don’t have special properties, so ungrouping is straightforward
- Only organizational structure changes
Common Patterns
Flatten Entire Hierarchy
// Remove all organizational layers
let modelsExist = true;
while (modelsExist) {
const models = await mcpClient.callTool('get_descendants', {
instancePath: 'game.Workspace.ComplexStructure',
classFilter: 'Model'
});
if (models.descendants.length === 0) {
modelsExist = false;
} else {
// Ungroup deepest first
for (const model of models.descendants.reverse()) {
await mcpClient.callTool('ungroup_objects', {
instancePath: model.path
});
}
}
}
console.log('Hierarchy completely flattened');
Conditional Ungrouping
// Ungroup only if group has few children
const children = await mcpClient.callTool('get_instance_children', {
instancePath: 'game.Workspace.SmallGroup'
});
if (children.children.length <= 3) {
await mcpClient.callTool('ungroup_objects', {
instancePath: 'game.Workspace.SmallGroup'
});
console.log('Small group ungrouped');
} else {
console.log('Group too large, keeping it');
}
Ungroup and Organize Differently
// Ungroup, then re-group by different criteria
const result = await mcpClient.callTool('ungroup_objects', {
instancePath: 'game.Workspace.OldOrganization'
});
// Get the moved objects and organize by class
const parts = [];
const scripts = [];
for (const obj of result.movedObjects) {
const props = await mcpClient.callTool('get_instance_properties', {
instancePath: obj.newPath
});
if (props.ClassName === 'Part') {
parts.push(obj.newPath);
} else if (props.ClassName === 'Script') {
scripts.push(obj.newPath);
}
}
// Re-group by type
await mcpClient.callTool('group_objects', {
paths: parts,
groupType: 'Folder',
groupName: 'Parts'
});
await mcpClient.callTool('group_objects', {
paths: scripts,
groupType: 'Folder',
groupName: 'Scripts'
});
Common Issues
Issue: Parts appear scattered after ungrouping
- Parts maintain world position, so they don’t move visually
- If it looks scattered, they were already scattered within the Model
- Use get_bounding_box before ungrouping to understand object distribution
Issue: Scripts break after ungrouping
- Scripts using Parent or FindFirstAncestor may break
- Scripts referencing the old group path need updating
- Check script source for hard-coded paths
Issue: Can’t ungroup certain containers
- Some system containers can’t be ungrouped (like Terrain, Camera)
- Only Models and Folders can be ungrouped
- Verify the instance class before ungrouping
Issue: Ungrouping doesn’t delete the container
- The container is only deleted if it’s empty after moving children
- If the container has properties or connections, it may remain
- Manually delete with delete_object if needed