using System;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace LocalMcp.UnityServer
{
/// <summary>
/// Provides Prefab operations for Unity MCP Server.
/// Handles prefab instantiation, creation, and management.
/// </summary>
public static class PrefabOperations
{
/// <summary>
/// Instantiates a prefab from an asset path.
/// </summary>
/// <param name="paramsJson">JSON parameters containing prefab path and optional parent/position</param>
/// <param name="id">JSON-RPC request ID</param>
/// <returns>JSON-RPC response with instantiated GameObject info</returns>
public static string InstantiatePrefab(string paramsJson, object id)
{
#if UNITY_EDITOR
try
{
var parameters = JsonUtility.FromJson<InstantiatePrefabParams>(paramsJson);
if (string.IsNullOrEmpty(parameters.prefabPath))
{
return JsonRpcResponseHelper.InvalidParams("prefabPath is required", id);
}
// Load prefab from asset path
GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(parameters.prefabPath);
if (prefab == null)
{
return JsonRpcResponseHelper.ErrorMessage($"Prefab not found at path: {parameters.prefabPath}", id);
}
// Instantiate prefab
GameObject instance = PrefabUtility.InstantiatePrefab(prefab) as GameObject;
if (instance == null)
{
return JsonRpcResponseHelper.ErrorMessage("Failed to instantiate prefab", id);
}
// Set name if provided
if (!string.IsNullOrEmpty(parameters.name))
{
instance.name = parameters.name;
}
// Set parent if provided
if (!string.IsNullOrEmpty(parameters.parentPath))
{
GameObject parent = GameObject.Find(parameters.parentPath);
if (parent != null)
{
instance.transform.SetParent(parent.transform, false);
}
}
// Set position if provided
if (parameters.position != null)
{
if (parameters.localPosition)
{
instance.transform.localPosition = new Vector3(
parameters.position.x,
parameters.position.y,
parameters.position.z
);
}
else
{
instance.transform.position = new Vector3(
parameters.position.x,
parameters.position.y,
parameters.position.z
);
}
}
// Set rotation if provided
if (parameters.rotation != null)
{
Quaternion rotation = Quaternion.Euler(
parameters.rotation.x,
parameters.rotation.y,
parameters.rotation.z
);
if (parameters.localPosition)
{
instance.transform.localRotation = rotation;
}
else
{
instance.transform.rotation = rotation;
}
}
// Build GameObject path
string path = GetGameObjectPath(instance);
var result = new InstantiatePrefabResult
{
success = true,
instancePath = path,
instanceName = instance.name,
instanceId = instance.GetInstanceID()
};
var response = JsonRpcResponse.Success(result, id);
return response.ToJson();
}
catch (Exception e)
{
Debug.LogError($"[MCP] InstantiatePrefab error: {e.Message}\n{e.StackTrace}");
return JsonRpcResponseHelper.ErrorMessage($"Failed to instantiate prefab: {e.Message}", id);
}
#else
return JsonRpcResponseHelper.ErrorMessage("InstantiatePrefab requires Unity Editor", id);
#endif
}
/// <summary>
/// Creates a prefab from a GameObject in the scene.
/// </summary>
/// <param name="paramsJson">JSON parameters containing gameObjectPath and prefab save path</param>
/// <param name="id">JSON-RPC request ID</param>
/// <returns>JSON-RPC response with prefab creation result</returns>
public static string CreatePrefab(string paramsJson, object id)
{
#if UNITY_EDITOR
try
{
var parameters = JsonUtility.FromJson<CreatePrefabParams>(paramsJson);
if (string.IsNullOrEmpty(parameters.gameObjectPath))
{
return JsonRpcResponseHelper.InvalidParams("gameObjectPath is required", id);
}
if (string.IsNullOrEmpty(parameters.prefabPath))
{
return JsonRpcResponseHelper.InvalidParams("prefabPath is required", id);
}
GameObject go = GameObject.Find(parameters.gameObjectPath);
if (go == null)
{
return JsonRpcResponseHelper.ErrorMessage($"GameObject '{parameters.gameObjectPath}' not found", id);
}
// Ensure path ends with .prefab
string prefabPath = parameters.prefabPath;
if (!prefabPath.EndsWith(".prefab"))
{
prefabPath += ".prefab";
}
// Create directory if needed
string directory = System.IO.Path.GetDirectoryName(prefabPath);
if (!string.IsNullOrEmpty(directory) && !System.IO.Directory.Exists(directory))
{
System.IO.Directory.CreateDirectory(directory);
}
// Create prefab
GameObject prefab = PrefabUtility.SaveAsPrefabAsset(go, prefabPath);
if (prefab == null)
{
return JsonRpcResponseHelper.ErrorMessage("Failed to create prefab", id);
}
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
var result = new CreatePrefabResult
{
success = true,
prefabPath = prefabPath,
prefabName = prefab.name
};
var response = JsonRpcResponse.Success(result, id);
return response.ToJson();
}
catch (Exception e)
{
Debug.LogError($"[MCP] CreatePrefab error: {e.Message}\n{e.StackTrace}");
return JsonRpcResponseHelper.ErrorMessage($"Failed to create prefab: {e.Message}", id);
}
#else
return JsonRpcResponseHelper.ErrorMessage("CreatePrefab requires Unity Editor", id);
#endif
}
/// <summary>
/// Applies changes from a prefab instance to the prefab asset.
/// </summary>
/// <param name="paramsJson">JSON parameters containing gameObjectPath</param>
/// <param name="id">JSON-RPC request ID</param>
/// <returns>JSON-RPC response</returns>
public static string ApplyPrefabChanges(string paramsJson, object id)
{
#if UNITY_EDITOR
try
{
var parameters = JsonUtility.FromJson<PrefabInstanceParams>(paramsJson);
if (string.IsNullOrEmpty(parameters.gameObjectPath))
{
return JsonRpcResponseHelper.InvalidParams("gameObjectPath is required", id);
}
GameObject go = GameObject.Find(parameters.gameObjectPath);
if (go == null)
{
return JsonRpcResponseHelper.ErrorMessage($"GameObject '{parameters.gameObjectPath}' not found", id);
}
// Check if it's a prefab instance
if (!PrefabUtility.IsPartOfPrefabInstance(go))
{
return JsonRpcResponseHelper.ErrorMessage($"GameObject '{parameters.gameObjectPath}' is not a prefab instance", id);
}
// Get prefab root
GameObject prefabRoot = PrefabUtility.GetOutermostPrefabInstanceRoot(go);
// Apply all overrides
PrefabUtility.ApplyPrefabInstance(prefabRoot, InteractionMode.AutomatedAction);
var result = new PrefabOperationResult
{
success = true,
message = $"Prefab changes applied for '{go.name}'"
};
var response = JsonRpcResponse.Success(result, id);
return response.ToJson();
}
catch (Exception e)
{
Debug.LogError($"[MCP] ApplyPrefabChanges error: {e.Message}\n{e.StackTrace}");
return JsonRpcResponseHelper.ErrorMessage($"Failed to apply prefab changes: {e.Message}", id);
}
#else
return JsonRpcResponseHelper.ErrorMessage("ApplyPrefabChanges requires Unity Editor", id);
#endif
}
/// <summary>
/// Reverts changes on a prefab instance to match the prefab asset.
/// </summary>
/// <param name="paramsJson">JSON parameters containing gameObjectPath</param>
/// <param name="id">JSON-RPC request ID</param>
/// <returns>JSON-RPC response</returns>
public static string RevertPrefabChanges(string paramsJson, object id)
{
#if UNITY_EDITOR
try
{
var parameters = JsonUtility.FromJson<PrefabInstanceParams>(paramsJson);
if (string.IsNullOrEmpty(parameters.gameObjectPath))
{
return JsonRpcResponseHelper.InvalidParams("gameObjectPath is required", id);
}
GameObject go = GameObject.Find(parameters.gameObjectPath);
if (go == null)
{
return JsonRpcResponseHelper.ErrorMessage($"GameObject '{parameters.gameObjectPath}' not found", id);
}
// Check if it's a prefab instance
if (!PrefabUtility.IsPartOfPrefabInstance(go))
{
return JsonRpcResponseHelper.ErrorMessage($"GameObject '{parameters.gameObjectPath}' is not a prefab instance", id);
}
// Get prefab root
GameObject prefabRoot = PrefabUtility.GetOutermostPrefabInstanceRoot(go);
// Revert all overrides
PrefabUtility.RevertPrefabInstance(prefabRoot, InteractionMode.AutomatedAction);
var result = new PrefabOperationResult
{
success = true,
message = $"Prefab changes reverted for '{go.name}'"
};
var response = JsonRpcResponse.Success(result, id);
return response.ToJson();
}
catch (Exception e)
{
Debug.LogError($"[MCP] RevertPrefabChanges error: {e.Message}\n{e.StackTrace}");
return JsonRpcResponseHelper.ErrorMessage($"Failed to revert prefab changes: {e.Message}", id);
}
#else
return JsonRpcResponseHelper.ErrorMessage("RevertPrefabChanges requires Unity Editor", id);
#endif
}
/// <summary>
/// Unpacks a prefab instance completely.
/// </summary>
/// <param name="paramsJson">JSON parameters containing gameObjectPath</param>
/// <param name="id">JSON-RPC request ID</param>
/// <returns>JSON-RPC response</returns>
public static string UnpackPrefab(string paramsJson, object id)
{
#if UNITY_EDITOR
try
{
var parameters = JsonUtility.FromJson<PrefabInstanceParams>(paramsJson);
if (string.IsNullOrEmpty(parameters.gameObjectPath))
{
return JsonRpcResponseHelper.InvalidParams("gameObjectPath is required", id);
}
GameObject go = GameObject.Find(parameters.gameObjectPath);
if (go == null)
{
return JsonRpcResponseHelper.ErrorMessage($"GameObject '{parameters.gameObjectPath}' not found", id);
}
// Check if it's a prefab instance
if (!PrefabUtility.IsPartOfPrefabInstance(go))
{
return JsonRpcResponseHelper.ErrorMessage($"GameObject '{parameters.gameObjectPath}' is not a prefab instance", id);
}
// Get prefab root
GameObject prefabRoot = PrefabUtility.GetOutermostPrefabInstanceRoot(go);
// Unpack prefab completely
PrefabUtility.UnpackPrefabInstance(prefabRoot, PrefabUnpackMode.Completely, InteractionMode.AutomatedAction);
var result = new PrefabOperationResult
{
success = true,
message = $"Prefab unpacked for '{go.name}'"
};
var response = JsonRpcResponse.Success(result, id);
return response.ToJson();
}
catch (Exception e)
{
Debug.LogError($"[MCP] UnpackPrefab error: {e.Message}\n{e.StackTrace}");
return JsonRpcResponseHelper.ErrorMessage($"Failed to unpack prefab: {e.Message}", id);
}
#else
return JsonRpcResponseHelper.ErrorMessage("UnpackPrefab requires Unity Editor", id);
#endif
}
private static string GetGameObjectPath(GameObject go)
{
string path = go.name;
Transform current = go.transform.parent;
while (current != null)
{
path = current.name + "/" + path;
current = current.parent;
}
return path;
}
}
#region Data Structures
[Serializable]
public class InstantiatePrefabParams
{
public string prefabPath;
public string name;
public string parentPath;
public Vector3Data position;
public Vector3Data rotation;
public bool localPosition = false;
}
[Serializable]
public class InstantiatePrefabResult
{
public bool success;
public string instancePath;
public string instanceName;
public int instanceId;
}
[Serializable]
public class CreatePrefabParams
{
public string gameObjectPath;
public string prefabPath;
}
[Serializable]
public class CreatePrefabResult
{
public bool success;
public string prefabPath;
public string prefabName;
}
[Serializable]
public class PrefabInstanceParams
{
public string gameObjectPath;
}
[Serializable]
public class PrefabOperationResult
{
public bool success;
public string message;
}
#endregion
}