Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 16 additions & 9 deletions MCPForUnity/Editor/Tools/Prefabs/ManagePrefabs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,15 @@ public static object HandleCommand(JObject @params)
return OpenPrefabStage(prefabPath);
}
case ACTION_SAVE_PREFAB_STAGE:
return SavePrefabStage();
{
bool refresh = @params["refresh"]?.ToObject<bool>() ?? true;
return SavePrefabStage(refresh);
}
case ACTION_CLOSE_PREFAB_STAGE:
{
bool saveBeforeClose = @params["saveBeforeClose"]?.ToObject<bool>() ?? false;
return ClosePrefabStage(saveBeforeClose);
bool refresh = @params["refresh"]?.ToObject<bool>() ?? true;
return ClosePrefabStage(saveBeforeClose, refresh);
}
default:
return new ErrorResponse($"Unknown action: '{action}'. Valid actions are: {SupportedActions}.");
Expand Down Expand Up @@ -1338,7 +1342,7 @@ private static object OpenPrefabStage(string requestedPath)
}
}

private static object SavePrefabStage()
private static object SavePrefabStage(bool refreshAfterSave = true)
{
try
{
Expand All @@ -1348,20 +1352,20 @@ private static object SavePrefabStage()
return new ErrorResponse("Not currently in prefab editing mode. Open a prefab stage first with open_prefab_stage.");
}

if (!TrySavePrefabStage(prefabStage, out string prefabPath, out string errorMessage))
if (!TrySavePrefabStage(prefabStage, refreshAfterSave, out string prefabPath, out string errorMessage))
{
return new ErrorResponse(errorMessage);
}

return new SuccessResponse($"Saved prefab stage changes for '{prefabPath}'.", new { prefabPath, saved = true });
return new SuccessResponse($"Saved prefab stage changes for '{prefabPath}'.", new { prefabPath, saved = true, refreshed = refreshAfterSave });
}
catch (Exception e)
{
return new ErrorResponse($"Error saving prefab stage: {e.Message}");
}
}

private static object ClosePrefabStage(bool saveBeforeClose = false)
private static object ClosePrefabStage(bool saveBeforeClose = false, bool refreshAfterSave = true)
{
try
{
Expand All @@ -1373,7 +1377,7 @@ private static object ClosePrefabStage(bool saveBeforeClose = false)

if (saveBeforeClose)
{
if (!TrySavePrefabStage(prefabStage, out _, out string errorMessage))
if (!TrySavePrefabStage(prefabStage, refreshAfterSave, out _, out string errorMessage))
{
return new ErrorResponse(errorMessage);
}
Expand All @@ -1389,7 +1393,7 @@ private static object ClosePrefabStage(bool saveBeforeClose = false)
}
}

private static bool TrySavePrefabStage(PrefabStage prefabStage, out string prefabPath, out string errorMessage)
private static bool TrySavePrefabStage(PrefabStage prefabStage, bool refreshAfterSave, out string prefabPath, out string errorMessage)
{
prefabPath = prefabStage.assetPath;
errorMessage = null;
Expand All @@ -1410,7 +1414,10 @@ private static bool TrySavePrefabStage(PrefabStage prefabStage, out string prefa

prefabStage.ClearDirtiness();
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
if (refreshAfterSave)
{
AssetDatabase.Refresh();
}
return true;
}

Expand Down
8 changes: 7 additions & 1 deletion Server/src/services/tools/manage_prefabs.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@
"Use component_properties with modify_contents to set serialized fields on existing components "
"(e.g. component_properties={\"Rigidbody\": {\"mass\": 5.0}, \"MyScript\": {\"health\": 100}}). "
"Supports object references via {\"guid\": \"...\"}, {\"path\": \"Assets/...\"}, or {\"instanceID\": 123}. "
"Use manage_asset action=search filterType=Prefab to list prefabs."
"Use manage_asset action=search filterType=Prefab to list prefabs. "
"Performance tip: for save_prefab_stage, pass refresh=false to skip the trailing AssetDatabase.Refresh() (often ~1s on large projects); SaveAssets() still runs so dirty assets are flushed. Call refresh_unity yourself when you actually need the asset database to reimport."
),
annotations=ToolAnnotations(
title="Manage Prefabs",
Expand Down Expand Up @@ -78,6 +79,7 @@ async def manage_prefabs(
create_child: Annotated[dict[str, Any] | list[dict[str, Any]], "Create child GameObject(s) in the prefab. Single object or array of objects, each with: name (required), parent (optional, defaults to target), source_prefab_path (optional: asset path to instantiate as nested prefab, e.g. 'Assets/Prefabs/Bullet.prefab'), primitive_type (optional: Cube, Sphere, Capsule, Cylinder, Plane, Quad), position, rotation, scale, components_to_add, tag, layer, set_active. source_prefab_path and primitive_type are mutually exclusive."] | None = None,
delete_child: Annotated[str | list[str], "Child name(s) or path(s) to remove from the prefab. Supports single string or array for batch deletion (e.g. 'Child1' or ['Child1', 'Child1/Grandchild'])."] | None = None,
component_properties: Annotated[dict[str, dict[str, Any]], "Set properties on existing components in modify_contents. Keys are component type names, values are dicts of property name to value. Example: {\"Rigidbody\": {\"mass\": 5.0}, \"MyScript\": {\"health\": 100}}. Supports object references via {\"guid\": \"...\"}, {\"path\": \"Assets/...\"}, or {\"instanceID\": 123}. For Sprite sub-assets: {\"guid\": \"...\", \"spriteName\": \"<name>\"}. Single-sprite textures auto-resolve."] | None = None,
refresh: Annotated[bool, "For save_prefab_stage: run AssetDatabase.Refresh() after writing the prefab (default true). Set false to skip the ~1s trailing import; SaveAssets() still runs so dirty assets are flushed. Call refresh_unity yourself when needed."] | None = None,
) -> dict[str, Any]:
# Back-compat: map 'name' → 'target' for create_from_gameobject (Unity accepts both)
if action == "create_from_gameobject" and target is None and name is not None:
Expand Down Expand Up @@ -198,6 +200,10 @@ def normalize_child_params(child: Any, index: int | None = None) -> tuple[dict |
if delete_child is not None:
params["deleteChild"] = delete_child

refresh_val = coerce_bool(refresh)
if refresh_val is not None:
params["refresh"] = refresh_val

# Send command to Unity
response = await send_with_unity_instance(
async_send_command_with_retry, unity_instance, "manage_prefabs", params
Expand Down
Loading