Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
103 changes: 89 additions & 14 deletions MCPForUnity/Editor/Tools/BatchExecute.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using MCPForUnity.Editor.Helpers;
using Newtonsoft.Json.Linq;
Expand Down Expand Up @@ -43,18 +44,20 @@ public static async Task<object> HandleCommand(JObject @params)
}

var commandResults = new List<object>(commandsToken.Count);
int successCount = 0;
int failureCount = 0;
int invocationSuccessCount = 0;
int invocationFailureCount = 0;
bool anyCommandFailed = false;

foreach (var token in commandsToken)
{
if (token is not JObject commandObj)
{
failureCount++;
invocationFailureCount++;
anyCommandFailed = true;
commandResults.Add(new
{
success = false,
tool = (string)null,
callSucceeded = false,
error = "Command entries must be JSON objects."
});
if (failFast)
Expand All @@ -65,15 +68,17 @@ public static async Task<object> HandleCommand(JObject @params)
}

string toolName = commandObj["tool"]?.ToString();
var commandParams = commandObj["params"] as JObject ?? new JObject();
var rawParams = commandObj["params"] as JObject ?? new JObject();
var commandParams = NormalizeParameterKeys(rawParams);

if (string.IsNullOrWhiteSpace(toolName))
{
failureCount++;
invocationFailureCount++;
anyCommandFailed = true;
commandResults.Add(new
{
success = false,
tool = toolName,
callSucceeded = false,
error = "Each command must include a non-empty 'tool' field."
});
if (failFast)
Expand All @@ -86,21 +91,23 @@ public static async Task<object> HandleCommand(JObject @params)
try
{
var result = await CommandRegistry.InvokeCommandAsync(toolName, commandParams).ConfigureAwait(true);
successCount++;
invocationSuccessCount++;

commandResults.Add(new
{
success = true,
tool = toolName,
callSucceeded = true,
result
});
}
catch (Exception ex)
{
failureCount++;
invocationFailureCount++;
anyCommandFailed = true;
commandResults.Add(new
{
success = false,
tool = toolName,
callSucceeded = false,
error = ex.Message
});

Expand All @@ -111,12 +118,12 @@ public static async Task<object> HandleCommand(JObject @params)
}
}

bool overallSuccess = failureCount == 0;
bool overallSuccess = !anyCommandFailed;
var data = new
{
results = commandResults,
successCount,
failureCount,
callSuccessCount = invocationSuccessCount,
callFailureCount = invocationFailureCount,
parallelRequested,
parallelApplied = false,
maxParallelism = maxParallel
Expand All @@ -126,5 +133,73 @@ public static async Task<object> HandleCommand(JObject @params)
? new SuccessResponse("Batch execution completed.", data)
: new ErrorResponse("One or more commands failed.", data);
}

private static JObject NormalizeParameterKeys(JObject source)
{
if (source == null)
{
return new JObject();
}

var normalized = new JObject();
foreach (var property in source.Properties())
{
string normalizedName = ToCamelCase(property.Name);
normalized[normalizedName] = NormalizeToken(property.Value);
}
return normalized;
}

private static JArray NormalizeArray(JArray source)
{
var normalized = new JArray();
foreach (var token in source)
{
normalized.Add(NormalizeToken(token));
}
return normalized;
}

private static JToken NormalizeToken(JToken token)
{
return token switch
{
JObject obj => NormalizeParameterKeys(obj),
JArray arr => NormalizeArray(arr),
_ => token.DeepClone()
};
}

private static string ToCamelCase(string key)
{
if (string.IsNullOrEmpty(key) || key.IndexOf('_') < 0)
{
return key;
}

var parts = key.Split(new[] { '_' }, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length == 0)
{
return key;
}

var builder = new StringBuilder(parts[0]);
for (int i = 1; i < parts.Length; i++)
{
var part = parts[i];
if (string.IsNullOrEmpty(part))
{
continue;
}

builder.Append(char.ToUpperInvariant(part[0]));
if (part.Length > 1)
{
builder.Append(part.AsSpan(1));
}
}

return builder.ToString();
}
}
}
11 changes: 11 additions & 0 deletions MCPForUnity/Editor/Tools/BatchExecute.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion MCPForUnity/Editor/Tools/ManageAsset.cs
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,9 @@ private static object ReimportAsset(string path, JObject properties)
private static object CreateAsset(JObject @params)
{
string path = @params["path"]?.ToString();
string assetType = @params["assetType"]?.ToString();
string assetType =
@params["assetType"]?.ToString()
?? @params["asset_type"]?.ToString(); // tolerate snake_case payloads from batched commands
JObject properties = @params["properties"] as JObject;

if (string.IsNullOrEmpty(path))
Expand Down
6 changes: 3 additions & 3 deletions Server/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading