using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
using System.Reflection;
#endif
namespace LocalMcp.UnityServer
{
#if UNITY_EDITOR
/// <summary>
/// Provides execution of Editor menu items and static methods
/// </summary>
public static class EditorMethodInvoker
{
/// <summary>
/// Executes a Unity Editor menu item
/// </summary>
public static string ExecuteMenuItem(string paramsJson, object id)
{
try
{
string menuPath = ExtractStringParam(paramsJson, "menuPath");
if (string.IsNullOrEmpty(menuPath))
{
return JsonRpcResponseHelper.ErrorMessage("menuPath parameter is required", id);
}
// Execute menu item (EditorApplication.ExecuteMenuItem returns false if menu item doesn't exist)
bool success = EditorApplication.ExecuteMenuItem(menuPath);
if (!success)
{
return JsonRpcResponseHelper.ErrorMessage($"Menu item does not exist or failed to execute: {menuPath}", id);
}
string result = $"{{\"success\": true, \"menuPath\": \"{menuPath}\", \"executed\": {success.ToString().ToLower()}}}";
return JsonRpcResponseHelper.SuccessMessage(result, id);
}
catch (System.Exception e)
{
return JsonRpcResponseHelper.ErrorMessage($"Failed to execute menu item: {e.Message}", id);
}
}
/// <summary>
/// Invokes a static method on a type
/// </summary>
public static string InvokeStaticMethod(string paramsJson, object id)
{
try
{
string typeName = ExtractStringParam(paramsJson, "typeName");
string methodName = ExtractStringParam(paramsJson, "methodName");
string parametersJson = ExtractJsonArray(paramsJson, "parameters");
if (string.IsNullOrEmpty(typeName) || string.IsNullOrEmpty(methodName))
{
return JsonRpcResponseHelper.ErrorMessage("typeName and methodName are required", id);
}
// Find the type
System.Type type = FindType(typeName);
if (type == null)
{
return JsonRpcResponseHelper.ErrorMessage($"Type not found: {typeName}", id);
}
// Parse parameters
object[] parameters = ParseParameters(parametersJson);
// Find the method
MethodInfo method = type.GetMethod(methodName, BindingFlags.Public | BindingFlags.Static);
if (method == null)
{
return JsonRpcResponseHelper.ErrorMessage($"Static method not found: {methodName} on type {typeName}", id);
}
// Invoke the method
object result = method.Invoke(null, parameters);
string resultJson = result != null ? $"{{\"success\": true, \"result\": \"{result}\" }}" : "{\"success\": true, \"result\": null}";
return JsonRpcResponseHelper.SuccessMessage(resultJson, id);
}
catch (System.Exception e)
{
return JsonRpcResponseHelper.ErrorMessage($"Failed to invoke static method: {e.Message}\n{e.StackTrace}", id);
}
}
/// <summary>
/// Lists all available menu items
/// </summary>
public static string ListMenuItems(string paramsJson, object id)
{
try
{
string filter = ExtractStringParam(paramsJson, "filter");
// Use reflection to get all menu items
var menuItems = new System.Collections.Generic.List<string>();
// Get all methods with MenuItem attribute
var assemblies = System.AppDomain.CurrentDomain.GetAssemblies();
foreach (var assembly in assemblies)
{
try
{
var types = assembly.GetTypes();
foreach (var type in types)
{
var methods = type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
foreach (var method in methods)
{
var attributes = method.GetCustomAttributes(typeof(MenuItem), false);
foreach (MenuItem menuItem in attributes)
{
string menuPath = menuItem.GetType().GetField("menuItem", BindingFlags.NonPublic | BindingFlags.Instance)?.GetValue(menuItem) as string;
if (menuPath != null)
{
if (string.IsNullOrEmpty(filter) || menuPath.Contains(filter))
{
menuItems.Add($"\"{menuPath}\"");
}
}
}
}
}
}
catch
{
// Skip assemblies that can't be loaded
}
}
string result = $"{{\"menuItems\": [{string.Join(",", menuItems)}], \"count\": {menuItems.Count}}}";
return JsonRpcResponseHelper.SuccessMessage(result, id);
}
catch (System.Exception e)
{
return JsonRpcResponseHelper.ErrorMessage($"Failed to list menu items: {e.Message}", id);
}
}
private static System.Type FindType(string typeName)
{
// Try direct Type.GetType first
System.Type type = System.Type.GetType(typeName);
if (type != null)
return type;
// Try with common assemblies
string[] assemblyNames = new string[]
{
"UnityEngine",
"UnityEngine.CoreModule",
"UnityEditor",
"UnityEditor.CoreModule",
"Assembly-CSharp",
"Assembly-CSharp-Editor",
"Assembly-CSharp-firstpass"
};
foreach (string assemblyName in assemblyNames)
{
type = System.Type.GetType($"{typeName}, {assemblyName}");
if (type != null)
return type;
}
// Try searching all loaded assemblies
foreach (System.Reflection.Assembly assembly in System.AppDomain.CurrentDomain.GetAssemblies())
{
type = assembly.GetType(typeName);
if (type != null)
return type;
}
return null;
}
private static object[] ParseParameters(string parametersJson)
{
if (string.IsNullOrEmpty(parametersJson))
{
return new object[0];
}
// Simple JSON array parser for basic types
parametersJson = parametersJson.Trim();
if (!parametersJson.StartsWith("[") || !parametersJson.EndsWith("]"))
{
return new object[0];
}
parametersJson = parametersJson.Substring(1, parametersJson.Length - 2);
if (string.IsNullOrEmpty(parametersJson))
{
return new object[0];
}
string[] parts = parametersJson.Split(',');
object[] parameters = new object[parts.Length];
for (int i = 0; i < parts.Length; i++)
{
string part = parts[i].Trim();
// Remove quotes for strings
if (part.StartsWith("\"") && part.EndsWith("\""))
{
parameters[i] = part.Substring(1, part.Length - 2);
}
// Parse numbers
else if (int.TryParse(part, out int intVal))
{
parameters[i] = intVal;
}
else if (float.TryParse(part, out float floatVal))
{
parameters[i] = floatVal;
}
// Parse booleans
else if (bool.TryParse(part, out bool boolVal))
{
parameters[i] = boolVal;
}
// Default to string
else
{
parameters[i] = part;
}
}
return parameters;
}
private static string ExtractStringParam(string paramsJson, string paramName)
{
try
{
string pattern = $"\"{paramName}\"\\s*:\\s*\"([^\"]+)\"";
var match = System.Text.RegularExpressions.Regex.Match(paramsJson, pattern);
if (match.Success)
{
return match.Groups[1].Value;
}
}
catch (System.Exception e)
{
Debug.LogError($"Error extracting parameter '{paramName}': {e.Message}");
}
return null;
}
private static string ExtractJsonArray(string paramsJson, string paramName)
{
try
{
int paramIndex = paramsJson.IndexOf($"\"{paramName}\"");
if (paramIndex == -1) return null;
int colonIndex = paramsJson.IndexOf(':', paramIndex);
if (colonIndex == -1) return null;
int startIndex = colonIndex + 1;
while (startIndex < paramsJson.Length && char.IsWhiteSpace(paramsJson[startIndex]))
{
startIndex++;
}
if (startIndex >= paramsJson.Length || paramsJson[startIndex] != '[')
{
return null;
}
int bracketCount = 1;
int endIndex = startIndex + 1;
while (endIndex < paramsJson.Length && bracketCount > 0)
{
if (paramsJson[endIndex] == '[') bracketCount++;
else if (paramsJson[endIndex] == ']') bracketCount--;
endIndex++;
}
return paramsJson.Substring(startIndex, endIndex - startIndex);
}
catch (System.Exception e)
{
Debug.LogError($"Error extracting array parameter '{paramName}': {e.Message}");
}
return null;
}
}
#endif
}