Skip to content

Commit 7f940db

Browse files
committed
feat: add AssetPathUtility for asset path normalization and update references in ManageAsset and ManagePrefabs
1 parent 08bb831 commit 7f940db

File tree

4 files changed

+66
-51
lines changed

4 files changed

+66
-51
lines changed
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
using System;
2+
3+
namespace MCPForUnity.Editor.Helpers
4+
{
5+
/// <summary>
6+
/// Provides common utility methods for working with Unity asset paths.
7+
/// </summary>
8+
public static class AssetPathUtility
9+
{
10+
/// <summary>
11+
/// Normalizes a Unity asset path by ensuring forward slashes are used and that it is rooted under "Assets/".
12+
/// </summary>
13+
public static string SanitizeAssetPath(string path)
14+
{
15+
if (string.IsNullOrEmpty(path))
16+
{
17+
return path;
18+
}
19+
20+
path = path.Replace('\\', '/');
21+
if (!path.StartsWith("Assets/", StringComparison.OrdinalIgnoreCase))
22+
{
23+
return "Assets/" + path.TrimStart('/');
24+
}
25+
26+
return path;
27+
}
28+
}
29+
}

UnityMcpBridge/Editor/Helpers/AssetPathUtility.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

UnityMcpBridge/Editor/Tools/ManageAsset.cs

Lines changed: 25 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ private static object ReimportAsset(string path, JObject properties)
115115
{
116116
if (string.IsNullOrEmpty(path))
117117
return Response.Error("'path' is required for reimport.");
118-
string fullPath = SanitizeAssetPath(path);
118+
string fullPath = AssetPathUtility.SanitizeAssetPath(path);
119119
if (!AssetExists(fullPath))
120120
return Response.Error($"Asset not found at path: {fullPath}");
121121

@@ -154,7 +154,7 @@ private static object CreateAsset(JObject @params)
154154
if (string.IsNullOrEmpty(assetType))
155155
return Response.Error("'assetType' is required for create.");
156156

157-
string fullPath = SanitizeAssetPath(path);
157+
string fullPath = AssetPathUtility.SanitizeAssetPath(path);
158158
string directory = Path.GetDirectoryName(fullPath);
159159

160160
// Ensure directory exists
@@ -280,7 +280,7 @@ private static object CreateFolder(string path)
280280
{
281281
if (string.IsNullOrEmpty(path))
282282
return Response.Error("'path' is required for create_folder.");
283-
string fullPath = SanitizeAssetPath(path);
283+
string fullPath = AssetPathUtility.SanitizeAssetPath(path);
284284
string parentDir = Path.GetDirectoryName(fullPath);
285285
string folderName = Path.GetFileName(fullPath);
286286

@@ -338,7 +338,7 @@ private static object ModifyAsset(string path, JObject properties)
338338
if (properties == null || !properties.HasValues)
339339
return Response.Error("'properties' are required for modify.");
340340

341-
string fullPath = SanitizeAssetPath(path);
341+
string fullPath = AssetPathUtility.SanitizeAssetPath(path);
342342
if (!AssetExists(fullPath))
343343
return Response.Error($"Asset not found at path: {fullPath}");
344344

@@ -372,7 +372,7 @@ prop.Value is JObject componentProperties
372372
{
373373
targetComponent = gameObject.GetComponent(compType);
374374
}
375-
375+
376376
// Only warn about resolution failure if component also not found
377377
if (targetComponent == null && !resolved)
378378
{
@@ -495,7 +495,7 @@ private static object DeleteAsset(string path)
495495
{
496496
if (string.IsNullOrEmpty(path))
497497
return Response.Error("'path' is required for delete.");
498-
string fullPath = SanitizeAssetPath(path);
498+
string fullPath = AssetPathUtility.SanitizeAssetPath(path);
499499
if (!AssetExists(fullPath))
500500
return Response.Error($"Asset not found at path: {fullPath}");
501501

@@ -526,7 +526,7 @@ private static object DuplicateAsset(string path, string destinationPath)
526526
if (string.IsNullOrEmpty(path))
527527
return Response.Error("'path' is required for duplicate.");
528528

529-
string sourcePath = SanitizeAssetPath(path);
529+
string sourcePath = AssetPathUtility.SanitizeAssetPath(path);
530530
if (!AssetExists(sourcePath))
531531
return Response.Error($"Source asset not found at path: {sourcePath}");
532532

@@ -538,7 +538,7 @@ private static object DuplicateAsset(string path, string destinationPath)
538538
}
539539
else
540540
{
541-
destPath = SanitizeAssetPath(destinationPath);
541+
destPath = AssetPathUtility.SanitizeAssetPath(destinationPath);
542542
if (AssetExists(destPath))
543543
return Response.Error($"Asset already exists at destination path: {destPath}");
544544
// Ensure destination directory exists
@@ -576,8 +576,8 @@ private static object MoveOrRenameAsset(string path, string destinationPath)
576576
if (string.IsNullOrEmpty(destinationPath))
577577
return Response.Error("'destination' path is required for move/rename.");
578578

579-
string sourcePath = SanitizeAssetPath(path);
580-
string destPath = SanitizeAssetPath(destinationPath);
579+
string sourcePath = AssetPathUtility.SanitizeAssetPath(path);
580+
string destPath = AssetPathUtility.SanitizeAssetPath(destinationPath);
581581

582582
if (!AssetExists(sourcePath))
583583
return Response.Error($"Source asset not found at path: {sourcePath}");
@@ -642,7 +642,7 @@ private static object SearchAssets(JObject @params)
642642
string[] folderScope = null;
643643
if (!string.IsNullOrEmpty(pathScope))
644644
{
645-
folderScope = new string[] { SanitizeAssetPath(pathScope) };
645+
folderScope = new string[] { AssetPathUtility.SanitizeAssetPath(pathScope) };
646646
if (!AssetDatabase.IsValidFolder(folderScope[0]))
647647
{
648648
// Maybe the user provided a file path instead of a folder?
@@ -732,7 +732,7 @@ private static object GetAssetInfo(string path, bool generatePreview)
732732
{
733733
if (string.IsNullOrEmpty(path))
734734
return Response.Error("'path' is required for get_info.");
735-
string fullPath = SanitizeAssetPath(path);
735+
string fullPath = AssetPathUtility.SanitizeAssetPath(path);
736736
if (!AssetExists(fullPath))
737737
return Response.Error($"Asset not found at path: {fullPath}");
738738

@@ -761,7 +761,7 @@ private static object GetComponentsFromAsset(string path)
761761
return Response.Error("'path' is required for get_components.");
762762

763763
// 2. Sanitize and check existence
764-
string fullPath = SanitizeAssetPath(path);
764+
string fullPath = AssetPathUtility.SanitizeAssetPath(path);
765765
if (!AssetExists(fullPath))
766766
return Response.Error($"Asset not found at path: {fullPath}");
767767

@@ -829,18 +829,6 @@ private static object GetComponentsFromAsset(string path)
829829
/// <summary>
830830
/// Ensures the asset path starts with "Assets/".
831831
/// </summary>
832-
private static string SanitizeAssetPath(string path)
833-
{
834-
if (string.IsNullOrEmpty(path))
835-
return path;
836-
path = path.Replace('\\', '/'); // Normalize separators
837-
if (!path.StartsWith("Assets/", StringComparison.OrdinalIgnoreCase))
838-
{
839-
return "Assets/" + path.TrimStart('/');
840-
}
841-
return path;
842-
}
843-
844832
/// <summary>
845833
/// Checks if an asset exists at the given path (file or folder).
846834
/// </summary>
@@ -930,16 +918,18 @@ private static bool ApplyMaterialProperties(Material mat, JObject properties)
930918
);
931919
}
932920
}
933-
} else if (properties["color"] is JArray colorArr) //Use color now with examples set in manage_asset.py
921+
}
922+
else if (properties["color"] is JArray colorArr) //Use color now with examples set in manage_asset.py
934923
{
935-
string propName = "_Color";
936-
try {
924+
string propName = "_Color";
925+
try
926+
{
937927
if (colorArr.Count >= 3)
938928
{
939929
Color newColor = new Color(
940930
colorArr[0].ToObject<float>(),
941-
colorArr[1].ToObject<float>(),
942-
colorArr[2].ToObject<float>(),
931+
colorArr[1].ToObject<float>(),
932+
colorArr[2].ToObject<float>(),
943933
colorArr.Count > 3 ? colorArr[3].ToObject<float>() : 1.0f
944934
);
945935
if (mat.HasProperty(propName) && mat.GetColor(propName) != newColor)
@@ -948,8 +938,9 @@ private static bool ApplyMaterialProperties(Material mat, JObject properties)
948938
modified = true;
949939
}
950940
}
951-
}
952-
catch (Exception ex) {
941+
}
942+
catch (Exception ex)
943+
{
953944
Debug.LogWarning(
954945
$"Error parsing color property '{propName}': {ex.Message}"
955946
);
@@ -989,7 +980,7 @@ private static bool ApplyMaterialProperties(Material mat, JObject properties)
989980
if (!string.IsNullOrEmpty(texPath))
990981
{
991982
Texture newTex = AssetDatabase.LoadAssetAtPath<Texture>(
992-
SanitizeAssetPath(texPath)
983+
AssetPathUtility.SanitizeAssetPath(texPath)
993984
);
994985
if (
995986
newTex != null
@@ -1217,7 +1208,7 @@ private static object ConvertJTokenToType(JToken token, Type targetType)
12171208
&& token.Type == JTokenType.String
12181209
)
12191210
{
1220-
string assetPath = SanitizeAssetPath(token.ToString());
1211+
string assetPath = AssetPathUtility.SanitizeAssetPath(token.ToString());
12211212
UnityEngine.Object loadedAsset = AssetDatabase.LoadAssetAtPath(
12221213
assetPath,
12231214
targetType
@@ -1337,4 +1328,3 @@ private static object GetAssetData(string path, bool generatePreview = false)
13371328
}
13381329
}
13391330
}
1340-

UnityMcpBridge/Editor/Tools/Prefabs/ManagePrefabs.cs

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ private static object OpenStage(JObject @params)
5757
return Response.Error("'path' parameter is required for open_stage.");
5858
}
5959

60-
string sanitizedPath = SanitizeAssetPath(path);
60+
string sanitizedPath = AssetPathUtility.SanitizeAssetPath(path);
6161
GameObject prefabAsset = AssetDatabase.LoadAssetAtPath<GameObject>(sanitizedPath);
6262
if (prefabAsset == null)
6363
{
@@ -257,20 +257,5 @@ private static object SerializeStage(PrefabStage stage)
257257
};
258258
}
259259

260-
private static string SanitizeAssetPath(string path)
261-
{
262-
if (string.IsNullOrEmpty(path))
263-
{
264-
return path;
265-
}
266-
267-
path = path.Replace('\\', '/');
268-
if (!path.StartsWith("Assets/", StringComparison.OrdinalIgnoreCase))
269-
{
270-
return "Assets/" + path.TrimStart('/');
271-
}
272-
273-
return path;
274-
}
275260
}
276261
}

0 commit comments

Comments
 (0)