Skip to main content
Glama
EditorMethodInvoker.cs11 kB
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 }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/dsgarage/UniMCP4CC'

If you have feedback or need assistance with the MCP directory API, please join our Discord server