From a17df1e4c91655c49e4527913e566639095d9dd0 Mon Sep 17 00:00:00 2001 From: Marcus Sanatan Date: Thu, 8 Jan 2026 01:44:32 -0400 Subject: [PATCH] feat(batch_execute): improve error handling with success detection and fail-fast support (#531) - Add DetermineCallSucceeded() to check command success via IMcpResponse interface or JObject/JToken 'success' field - Track invocationFailureCount and set anyCommandFailed flag when commands fail - Implement fail-fast behavior to stop batch execution on first failure when failFast=true - Update commandResults to use computed callSucceeded value instead of hardcoded true feat(game_object_create): enhance asset --- MCPForUnity/Editor/Tools/BatchExecute.cs | 51 ++++++++++++++++++- .../Tools/GameObjects/GameObjectCreate.cs | 39 ++++++++++++-- 2 files changed, 83 insertions(+), 7 deletions(-) diff --git a/MCPForUnity/Editor/Tools/BatchExecute.cs b/MCPForUnity/Editor/Tools/BatchExecute.cs index fa46dd3..c944ace 100644 --- a/MCPForUnity/Editor/Tools/BatchExecute.cs +++ b/MCPForUnity/Editor/Tools/BatchExecute.cs @@ -91,14 +91,28 @@ namespace MCPForUnity.Editor.Tools try { var result = await CommandRegistry.InvokeCommandAsync(toolName, commandParams).ConfigureAwait(true); - invocationSuccessCount++; + bool callSucceeded = DetermineCallSucceeded(result); + if (callSucceeded) + { + invocationSuccessCount++; + } + else + { + invocationFailureCount++; + anyCommandFailed = true; + } commandResults.Add(new { tool = toolName, - callSucceeded = true, + callSucceeded, result }); + + if (!callSucceeded && failFast) + { + break; + } } catch (Exception ex) { @@ -134,6 +148,39 @@ namespace MCPForUnity.Editor.Tools : new ErrorResponse("One or more commands failed.", data); } + private static bool DetermineCallSucceeded(object result) + { + if (result == null) + { + return true; + } + + if (result is IMcpResponse response) + { + return response.Success; + } + + if (result is JObject obj) + { + var successToken = obj["success"]; + if (successToken != null && successToken.Type == JTokenType.Boolean) + { + return successToken.Value(); + } + } + + if (result is JToken token) + { + var successToken = token["success"]; + if (successToken != null && successToken.Type == JTokenType.Boolean) + { + return successToken.Value(); + } + } + + return true; + } + private static JObject NormalizeParameterKeys(JObject source) { if (source == null) diff --git a/MCPForUnity/Editor/Tools/GameObjects/GameObjectCreate.cs b/MCPForUnity/Editor/Tools/GameObjects/GameObjectCreate.cs index a94ed10..6954a26 100644 --- a/MCPForUnity/Editor/Tools/GameObjects/GameObjectCreate.cs +++ b/MCPForUnity/Editor/Tools/GameObjects/GameObjectCreate.cs @@ -29,9 +29,11 @@ namespace MCPForUnity.Editor.Tools.GameObjects // --- Try Instantiating Prefab First --- string originalPrefabPath = prefabPath; - if (!string.IsNullOrEmpty(prefabPath)) + if (!saveAsPrefab && !string.IsNullOrEmpty(prefabPath)) { - if (!prefabPath.Contains("/") && !prefabPath.EndsWith(".prefab", StringComparison.OrdinalIgnoreCase)) + string extension = System.IO.Path.GetExtension(prefabPath); + + if (!prefabPath.Contains("/") && (string.IsNullOrEmpty(extension) || extension.Equals(".prefab", StringComparison.OrdinalIgnoreCase))) { string prefabNameOnly = prefabPath; McpLog.Info($"[ManageGameObject.Create] Searching for prefab named: '{prefabNameOnly}'"); @@ -51,11 +53,38 @@ namespace MCPForUnity.Editor.Tools.GameObjects McpLog.Info($"[ManageGameObject.Create] Found unique prefab at path: '{prefabPath}'"); } } - else if (!prefabPath.EndsWith(".prefab", StringComparison.OrdinalIgnoreCase)) + else if (prefabPath.Contains("/") && string.IsNullOrEmpty(extension)) { - McpLog.Warn($"[ManageGameObject.Create] Provided prefabPath '{prefabPath}' does not end with .prefab. Assuming it's missing and appending."); + McpLog.Warn($"[ManageGameObject.Create] Provided prefabPath '{prefabPath}' has no extension. Assuming it's a prefab and appending .prefab."); prefabPath += ".prefab"; } + else if (!prefabPath.Contains("/") && !string.IsNullOrEmpty(extension) && !extension.Equals(".prefab", StringComparison.OrdinalIgnoreCase)) + { + string fileName = prefabPath; + string fileNameWithoutExtension = System.IO.Path.GetFileNameWithoutExtension(fileName); + McpLog.Info($"[ManageGameObject.Create] Searching for asset file named: '{fileName}'"); + + string[] guids = AssetDatabase.FindAssets(fileNameWithoutExtension); + var matches = guids + .Select(g => AssetDatabase.GUIDToAssetPath(g)) + .Where(p => p.EndsWith("/" + fileName, StringComparison.OrdinalIgnoreCase) || p.Equals(fileName, StringComparison.OrdinalIgnoreCase)) + .ToArray(); + + if (matches.Length == 0) + { + return new ErrorResponse($"Asset file '{fileName}' not found anywhere in the project."); + } + else if (matches.Length > 1) + { + string foundPaths = string.Join(", ", matches); + return new ErrorResponse($"Multiple assets found matching file name '{fileName}': {foundPaths}. Please provide a more specific path."); + } + else + { + prefabPath = matches[0]; + McpLog.Info($"[ManageGameObject.Create] Found unique asset at path: '{prefabPath}'"); + } + } GameObject prefabAsset = AssetDatabase.LoadAssetAtPath(prefabPath); if (prefabAsset != null) @@ -83,7 +112,7 @@ namespace MCPForUnity.Editor.Tools.GameObjects } else { - McpLog.Warn($"[ManageGameObject.Create] Prefab asset not found at path: '{prefabPath}'. Will proceed to create new object if specified."); + return new ErrorResponse($"Asset not found or not a GameObject at path: '{prefabPath}'."); } }