using System;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace LocalMcp.UnityServer
{
/// <summary>
/// Provides AssetDatabase operations for Unity MCP Server.
/// Handles asset searching, listing, and metadata retrieval.
/// </summary>
public static class AssetDatabaseProvider
{
/// <summary>
/// Lists all assets of a specific type in the project or a specific path.
/// </summary>
/// <param name="paramsJson">JSON parameters containing assetType and optional path</param>
/// <param name="id">JSON-RPC request ID</param>
/// <returns>JSON-RPC response containing list of assets</returns>
public static string ListAssets(string paramsJson, object id)
{
#if UNITY_EDITOR
try
{
var parameters = JsonUtility.FromJson<ListAssetsParams>(paramsJson);
if (string.IsNullOrEmpty(parameters.assetType))
{
return JsonRpcResponseHelper.InvalidParams("assetType is required", id);
}
string searchPath = string.IsNullOrEmpty(parameters.path) ? "Assets" : parameters.path;
string filter = $"t:{parameters.assetType}";
// Find all assets of the specified type
string[] guids = AssetDatabase.FindAssets(filter, new[] { searchPath });
List<AssetInfo> assets = new List<AssetInfo>();
foreach (string guid in guids)
{
string assetPath = AssetDatabase.GUIDToAssetPath(guid);
UnityEngine.Object asset = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(assetPath);
if (asset != null)
{
assets.Add(new AssetInfo
{
name = asset.name,
path = assetPath,
guid = guid,
type = asset.GetType().Name
});
}
}
var result = new ListAssetsResult
{
assets = assets.ToArray(),
total = assets.Count
};
var response = JsonRpcResponse.Success(result, id);
return response.ToJson();
}
catch (Exception e)
{
Debug.LogError($"[MCP] ListAssets error: {e.Message}\n{e.StackTrace}");
return JsonRpcResponseHelper.ErrorMessage($"Failed to list assets: {e.Message}", id);
}
#else
return JsonRpcResponseHelper.ErrorMessage("ListAssets requires Unity Editor", id);
#endif
}
/// <summary>
/// Finds a specific asset by path or GUID.
/// </summary>
/// <param name="paramsJson">JSON parameters containing path or guid</param>
/// <param name="id">JSON-RPC request ID</param>
/// <returns>JSON-RPC response containing asset information or not found status</returns>
public static string FindAsset(string paramsJson, object id)
{
#if UNITY_EDITOR
try
{
var parameters = JsonUtility.FromJson<FindAssetParams>(paramsJson);
string assetPath = null;
// Resolve path from GUID if provided
if (!string.IsNullOrEmpty(parameters.guid))
{
assetPath = AssetDatabase.GUIDToAssetPath(parameters.guid);
}
else if (!string.IsNullOrEmpty(parameters.path))
{
assetPath = parameters.path;
}
else
{
return JsonRpcResponseHelper.InvalidParams("Either 'path' or 'guid' parameter is required", id);
}
// Check if asset exists
if (string.IsNullOrEmpty(assetPath) || !File.Exists(assetPath))
{
var notFoundResult = new FindAssetResult
{
found = false
};
var notFoundResponse = JsonRpcResponse.Success(notFoundResult, id);
return notFoundResponse.ToJson();
}
// Load asset
UnityEngine.Object asset = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(assetPath);
if (asset == null)
{
var notFoundResult = new FindAssetResult
{
found = false
};
var notFoundResponse = JsonRpcResponse.Success(notFoundResult, id);
return notFoundResponse.ToJson();
}
// Get asset metadata
string guid = AssetDatabase.AssetPathToGUID(assetPath);
FileInfo fileInfo = new FileInfo(assetPath);
var assetInfo = new AssetInfoDetailed
{
name = asset.name,
path = assetPath,
guid = guid,
type = asset.GetType().Name,
fileSize = fileInfo.Length,
lastModified = fileInfo.LastWriteTimeUtc.ToString("o")
};
var result = new FindAssetResult
{
found = true,
asset = assetInfo
};
var response = JsonRpcResponse.Success(result, id);
return response.ToJson();
}
catch (Exception e)
{
Debug.LogError($"[MCP] FindAsset error: {e.Message}\n{e.StackTrace}");
return JsonRpcResponseHelper.ErrorMessage($"Failed to find asset: {e.Message}", id);
}
#else
return JsonRpcResponseHelper.ErrorMessage("FindAsset requires Unity Editor", id);
#endif
}
/// <summary>
/// Refreshes the AssetDatabase to detect file changes and recompile scripts.
/// </summary>
/// <param name="paramsJson">JSON parameters (optional: importMode)</param>
/// <param name="id">JSON-RPC request ID</param>
/// <returns>JSON-RPC response</returns>
public static string RefreshAssetDatabase(string paramsJson, object id)
{
#if UNITY_EDITOR
try
{
var parameters = JsonUtility.FromJson<RefreshParams>(paramsJson);
ImportAssetOptions importOptions = ImportAssetOptions.Default;
// Parse import mode
if (!string.IsNullOrEmpty(parameters.importMode))
{
switch (parameters.importMode.ToLower())
{
case "forcesynchronousimport":
importOptions = ImportAssetOptions.ForceSynchronousImport;
break;
case "forceupdate":
importOptions = ImportAssetOptions.ForceUpdate;
break;
case "importrecursive":
importOptions = ImportAssetOptions.ImportRecursive;
break;
default:
importOptions = ImportAssetOptions.Default;
break;
}
}
Debug.Log($"[MCP] Refreshing AssetDatabase with mode: {importOptions}");
// Refresh the AssetDatabase
AssetDatabase.Refresh(importOptions);
var result = new RefreshResult
{
success = true,
message = "AssetDatabase refreshed successfully",
importMode = importOptions.ToString()
};
var response = JsonRpcResponse.Success(result, id);
return response.ToJson();
}
catch (Exception e)
{
Debug.LogError($"[MCP] RefreshAssetDatabase error: {e.Message}\n{e.StackTrace}");
return JsonRpcResponseHelper.ErrorMessage($"Failed to refresh AssetDatabase: {e.Message}", id);
}
#else
return JsonRpcResponseHelper.ErrorMessage("RefreshAssetDatabase requires Unity Editor", id);
#endif
}
/// <summary>
/// Deletes an asset from the project.
/// </summary>
/// <param name="paramsJson">JSON parameters containing assetPath</param>
/// <param name="id">JSON-RPC request ID</param>
/// <returns>JSON-RPC response</returns>
public static string DeleteAsset(string paramsJson, object id)
{
#if UNITY_EDITOR
try
{
var parameters = JsonUtility.FromJson<AssetPathParams>(paramsJson);
if (string.IsNullOrEmpty(parameters.assetPath))
{
return JsonRpcResponseHelper.InvalidParams("assetPath is required", id);
}
// Check if asset exists
if (!AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(parameters.assetPath))
{
return JsonRpcResponseHelper.ErrorMessage($"Asset not found at path: {parameters.assetPath}", id);
}
// Delete the asset
bool success = AssetDatabase.DeleteAsset(parameters.assetPath);
if (!success)
{
return JsonRpcResponseHelper.ErrorMessage($"Failed to delete asset: {parameters.assetPath}", id);
}
var result = new AssetOperationResult
{
success = true,
message = $"Asset deleted: {parameters.assetPath}",
assetPath = parameters.assetPath
};
var response = JsonRpcResponse.Success(result, id);
return response.ToJson();
}
catch (Exception e)
{
Debug.LogError($"[MCP] DeleteAsset error: {e.Message}\n{e.StackTrace}");
return JsonRpcResponseHelper.ErrorMessage($"Failed to delete asset: {e.Message}", id);
}
#else
return JsonRpcResponseHelper.ErrorMessage("DeleteAsset requires Unity Editor", id);
#endif
}
/// <summary>
/// Moves an asset to a new path.
/// </summary>
/// <param name="paramsJson">JSON parameters containing oldPath and newPath</param>
/// <param name="id">JSON-RPC request ID</param>
/// <returns>JSON-RPC response</returns>
public static string MoveAsset(string paramsJson, object id)
{
#if UNITY_EDITOR
try
{
var parameters = JsonUtility.FromJson<MoveAssetParams>(paramsJson);
if (string.IsNullOrEmpty(parameters.oldPath))
{
return JsonRpcResponseHelper.InvalidParams("oldPath is required", id);
}
if (string.IsNullOrEmpty(parameters.newPath))
{
return JsonRpcResponseHelper.InvalidParams("newPath is required", id);
}
// Check if source asset exists
if (!AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(parameters.oldPath))
{
return JsonRpcResponseHelper.ErrorMessage($"Asset not found at path: {parameters.oldPath}", id);
}
// Check if destination already exists
if (AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(parameters.newPath))
{
return JsonRpcResponseHelper.ErrorMessage($"Asset already exists at destination: {parameters.newPath}", id);
}
// Move the asset
string error = AssetDatabase.MoveAsset(parameters.oldPath, parameters.newPath);
if (!string.IsNullOrEmpty(error))
{
return JsonRpcResponseHelper.ErrorMessage($"Failed to move asset: {error}", id);
}
var result = new AssetOperationResult
{
success = true,
message = $"Asset moved from {parameters.oldPath} to {parameters.newPath}",
assetPath = parameters.newPath
};
var response = JsonRpcResponse.Success(result, id);
return response.ToJson();
}
catch (Exception e)
{
Debug.LogError($"[MCP] MoveAsset error: {e.Message}\n{e.StackTrace}");
return JsonRpcResponseHelper.ErrorMessage($"Failed to move asset: {e.Message}", id);
}
#else
return JsonRpcResponseHelper.ErrorMessage("MoveAsset requires Unity Editor", id);
#endif
}
/// <summary>
/// Copies an asset to a new path.
/// </summary>
/// <param name="paramsJson">JSON parameters containing sourcePath and destinationPath</param>
/// <param name="id">JSON-RPC request ID</param>
/// <returns>JSON-RPC response</returns>
public static string CopyAsset(string paramsJson, object id)
{
#if UNITY_EDITOR
try
{
var parameters = JsonUtility.FromJson<CopyAssetParams>(paramsJson);
if (string.IsNullOrEmpty(parameters.sourcePath))
{
return JsonRpcResponseHelper.InvalidParams("sourcePath is required", id);
}
if (string.IsNullOrEmpty(parameters.destinationPath))
{
return JsonRpcResponseHelper.InvalidParams("destinationPath is required", id);
}
// Check if source asset exists
if (!AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(parameters.sourcePath))
{
return JsonRpcResponseHelper.ErrorMessage($"Asset not found at path: {parameters.sourcePath}", id);
}
// Check if destination already exists
if (AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(parameters.destinationPath))
{
return JsonRpcResponseHelper.ErrorMessage($"Asset already exists at destination: {parameters.destinationPath}", id);
}
// Copy the asset
bool success = AssetDatabase.CopyAsset(parameters.sourcePath, parameters.destinationPath);
if (!success)
{
return JsonRpcResponseHelper.ErrorMessage($"Failed to copy asset from {parameters.sourcePath} to {parameters.destinationPath}", id);
}
var result = new AssetOperationResult
{
success = true,
message = $"Asset copied from {parameters.sourcePath} to {parameters.destinationPath}",
assetPath = parameters.destinationPath
};
var response = JsonRpcResponse.Success(result, id);
return response.ToJson();
}
catch (Exception e)
{
Debug.LogError($"[MCP] CopyAsset error: {e.Message}\n{e.StackTrace}");
return JsonRpcResponseHelper.ErrorMessage($"Failed to copy asset: {e.Message}", id);
}
#else
return JsonRpcResponseHelper.ErrorMessage("CopyAsset requires Unity Editor", id);
#endif
}
}
#region Data Structures
/// <summary>
/// Parameters for listing assets.
/// </summary>
[Serializable]
public class ListAssetsParams
{
public string assetType;
public string path;
}
/// <summary>
/// Parameters for finding an asset.
/// </summary>
[Serializable]
public class FindAssetParams
{
public string path;
public string guid;
}
/// <summary>
/// Basic asset information.
/// </summary>
[Serializable]
public class AssetInfo
{
public string name;
public string path;
public string guid;
public string type;
}
/// <summary>
/// Detailed asset information including file metadata.
/// </summary>
[Serializable]
public class AssetInfoDetailed
{
public string name;
public string path;
public string guid;
public string type;
public long fileSize;
public string lastModified;
}
/// <summary>
/// Result of listing assets.
/// </summary>
[Serializable]
public class ListAssetsResult
{
public AssetInfo[] assets;
public int total;
}
/// <summary>
/// Result of finding an asset.
/// </summary>
[Serializable]
public class FindAssetResult
{
public bool found;
public AssetInfoDetailed asset;
}
/// <summary>
/// Parameters for refreshing AssetDatabase.
/// </summary>
[Serializable]
public class RefreshParams
{
public string importMode; // "Default", "ForceSynchronousImport", "ForceUpdate", "ImportRecursive"
}
/// <summary>
/// Result of refreshing AssetDatabase.
/// </summary>
[Serializable]
public class RefreshResult
{
public bool success;
public string message;
public string importMode;
}
/// <summary>
/// Parameters for asset path operations.
/// </summary>
[Serializable]
public class AssetPathParams
{
public string assetPath;
}
/// <summary>
/// Parameters for moving an asset.
/// </summary>
[Serializable]
public class MoveAssetParams
{
public string oldPath;
public string newPath;
}
/// <summary>
/// Parameters for copying an asset.
/// </summary>
[Serializable]
public class CopyAssetParams
{
public string sourcePath;
public string destinationPath;
}
/// <summary>
/// Result of asset operations (delete, move, copy).
/// </summary>
[Serializable]
public class AssetOperationResult
{
public bool success;
public string message;
public string assetPath;
}
#endregion
}