Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
8b10c6f
feat: Add prefab read operations (get_info, get_hierarchy, list_prefabs)
whatevertogo Jan 22, 2026
d0cb1ba
fix: Use correct API to save prefab stage changes
whatevertogo Jan 22, 2026
94a78ec
refactor: improve code quality and error handling
whatevertogo Jan 22, 2026
8493558
feat: Remove list_prefabs action and update related documentation
whatevertogo Jan 22, 2026
5859fec
feat: Enhance prefab management with detailed parameter descriptions …
whatevertogo Jan 22, 2026
6a59050
feat: Simplify prefab creation logic and unify logging for asset repl…
whatevertogo Jan 22, 2026
943e7ad
feat: Update SaveStagePrefab method to use SetDirty and SaveAssets fo…
whatevertogo Jan 22, 2026
228f9fb
feat: Add PrefabUtilityHelper class with utility methods for prefab a…
whatevertogo Jan 22, 2026
2e61b21
feat: Refactor action constants and enhance parameter validation in p…
whatevertogo Jan 22, 2026
aac6ec8
feat: Update ValidateSourceObjectForPrefab method to remove replaceEx…
whatevertogo Jan 22, 2026
f44e1b3
fix: Fix searchInactive parameter and improve prefab management
whatevertogo Jan 22, 2026
14e3847
feat: Add path validation and security checks for prefab operations
whatevertogo Jan 23, 2026
c21c860
feat: Remove pagination from GetHierarchy method and simplify prefab …
whatevertogo Jan 23, 2026
40de0d0
feat: Remove mode parameter from prefab management functions to simpl…
whatevertogo Jan 23, 2026
72de29d
fix: Improve path validation and replace logic in prefab management
whatevertogo Jan 23, 2026
d0c7135
feat: Enhance prefab management by adding nesting depth and parent pr…
whatevertogo Jan 23, 2026
aa8d358
Merge branch 'CoplayDev:beta' into enhancement/Prefab-management
whatevertogo Jan 23, 2026
b74cc00
fix: resolve Unknown pseudo class last-child USS warnings
dsarno Jan 25, 2026
f0a2cc5
Merge branch 'enhancement/Prefab-management' of https://github.com/wh…
dsarno Jan 25, 2026
7c8d478
fix: improve prefab stage save for automated workflows
dsarno Jan 25, 2026
b1471de
Merge branch 'CoplayDev:beta' into enhancement/Prefab-management
whatevertogo Jan 25, 2026
56a8d23
Merge pull request #1 from dsarno/fix/prefab-save-dialogs-and-force-p…
whatevertogo Jan 25, 2026
2ccf8f1
Update prefab.py
whatevertogo Jan 25, 2026
e4d3c8c
refactor: remove unnecessary blank line before create function
whatevertogo Jan 25, 2026
f752290
feat: add info and hierarchy commands to prefab CLI for enhanced pref…
whatevertogo Jan 25, 2026
1bcc6e1
feat: enhance prefab management with comprehensive CRUD tests and ens…
whatevertogo Jan 25, 2026
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
49 changes: 49 additions & 0 deletions MCPForUnity/Editor/Helpers/AssetPathUtility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public static string NormalizeSeparators(string path)

/// <summary>
/// Normalizes a Unity asset path by ensuring forward slashes are used and that it is rooted under "Assets/".
/// Also protects against path traversal attacks using "../" sequences.
/// </summary>
public static string SanitizeAssetPath(string path)
{
Expand All @@ -36,6 +37,15 @@ public static string SanitizeAssetPath(string path)
}

path = NormalizeSeparators(path);

// Check for path traversal sequences
if (path.Contains(".."))
{
McpLog.Warn($"[AssetPathUtility] Path contains potential traversal sequence: '{path}'");
return null;
}

// Ensure path starts with Assets/
if (!path.StartsWith("Assets/", StringComparison.OrdinalIgnoreCase))
{
return "Assets/" + path.TrimStart('/');
Expand All @@ -44,6 +54,45 @@ public static string SanitizeAssetPath(string path)
return path;
}

/// <summary>
/// Checks if a given asset path is valid and safe (no traversal, within Assets folder).
/// </summary>
/// <returns>True if the path is valid, false otherwise.</returns>
public static bool IsValidAssetPath(string path)
{
if (string.IsNullOrEmpty(path))
{
return false;
}

// Normalize for comparison
string normalized = NormalizeSeparators(path);

// Must start with Assets/
if (!normalized.StartsWith("Assets/", StringComparison.OrdinalIgnoreCase))
{
return false;
}

// Must not contain traversal sequences
if (normalized.Contains(".."))
{
return false;
}

// Must not contain invalid path characters
char[] invalidChars = { ':', '*', '?', '"', '<', '>', '|' };
foreach (char c in invalidChars)
{
if (normalized.IndexOf(c) >= 0)
{
return false;
}
}
Comment thread
whatevertogo marked this conversation as resolved.

return true;
}

/// <summary>
/// Gets the MCP for Unity package root path.
/// Works for registry Package Manager, local Package Manager, and Asset Store installations.
Expand Down
155 changes: 155 additions & 0 deletions MCPForUnity/Editor/Helpers/PrefabUtilityHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

namespace MCPForUnity.Editor.Helpers
{
/// <summary>
/// Provides common utility methods for working with Unity Prefab assets.
/// </summary>
public static class PrefabUtilityHelper
{
/// <summary>
/// Gets the GUID for a prefab asset path.
/// </summary>
/// <param name="assetPath">The Unity asset path (e.g., "Assets/Prefabs/MyPrefab.prefab")</param>
/// <returns>The GUID string, or null if the path is invalid.</returns>
public static string GetPrefabGUID(string assetPath)
{
if (string.IsNullOrEmpty(assetPath))
{
return null;
}

try
{
return AssetDatabase.AssetPathToGUID(assetPath);
}
catch (Exception ex)
{
McpLog.Warn($"Failed to get GUID for asset path '{assetPath}': {ex.Message}");
return null;
}
}

/// <summary>
/// Gets variant information if the prefab is a variant.
/// </summary>
/// <param name="prefabAsset">The prefab GameObject to check.</param>
/// <returns>A tuple containing (isVariant, parentPath, parentGuid).</returns>
public static (bool isVariant, string parentPath, string parentGuid) GetVariantInfo(GameObject prefabAsset)
{
if (prefabAsset == null)
{
return (false, null, null);
}

try
{
PrefabAssetType assetType = PrefabUtility.GetPrefabAssetType(prefabAsset);
if (assetType != PrefabAssetType.Variant)
{
return (false, null, null);
}

GameObject parentAsset = PrefabUtility.GetCorrespondingObjectFromSource(prefabAsset);
if (parentAsset == null)
{
return (true, null, null);
}

string parentPath = AssetDatabase.GetAssetPath(parentAsset);
string parentGuid = GetPrefabGUID(parentPath);

return (true, parentPath, parentGuid);
}
catch (Exception ex)
{
McpLog.Warn($"Failed to get variant info for '{prefabAsset.name}': {ex.Message}");
return (false, null, null);
}
}

/// <summary>
/// Gets the list of component type names on a GameObject.
/// </summary>
/// <param name="obj">The GameObject to inspect.</param>
/// <returns>A list of component type full names.</returns>
public static List<string> GetComponentTypeNames(GameObject obj)
{
var typeNames = new List<string>();

if (obj == null)
{
return typeNames;
}

try
{
var components = obj.GetComponents<Component>();
foreach (var component in components)
{
if (component != null)
{
typeNames.Add(component.GetType().FullName);
}
}
}
catch (Exception ex)
{
McpLog.Warn($"Failed to get component types for '{obj.name}': {ex.Message}");
}

return typeNames;
}

/// <summary>
/// Recursively counts all children in the hierarchy.
/// </summary>
/// <param name="transform">The root transform to count from.</param>
/// <returns>Total number of children in the hierarchy.</returns>
public static int CountChildrenRecursive(Transform transform)
{
if (transform == null)
{
return 0;
}

int count = transform.childCount;
for (int i = 0; i < transform.childCount; i++)
{
count += CountChildrenRecursive(transform.GetChild(i));
}
return count;
}

/// <summary>
/// Gets the source prefab path for a nested prefab instance.
/// </summary>
/// <param name="gameObject">The GameObject to check.</param>
/// <returns>The asset path of the source prefab, or null if not a nested prefab.</returns>
public static string GetNestedPrefabPath(GameObject gameObject)
{
if (gameObject == null || !PrefabUtility.IsAnyPrefabInstanceRoot(gameObject))
{
return null;
}

try
{
var sourcePrefab = PrefabUtility.GetCorrespondingObjectFromSource(gameObject);
if (sourcePrefab != null)
{
return AssetDatabase.GetAssetPath(sourcePrefab);
}
}
catch (Exception ex)
{
McpLog.Warn($"Failed to get nested prefab path for '{gameObject.name}': {ex.Message}");
}

return null;
}
}
}
Loading