diff --git a/Editor/Commands/ObjectCommandHandler.cs b/Editor/Commands/ObjectCommandHandler.cs
index eecfcf2..6176b28 100644
--- a/Editor/Commands/ObjectCommandHandler.cs
+++ b/Editor/Commands/ObjectCommandHandler.cs
@@ -7,6 +7,7 @@ using UnityEditor;
using UnityEngine.SceneManagement;
using UnityEditor.SceneManagement;
using UnityMCP.Editor.Helpers;
+using System.Reflection;
namespace UnityMCP.Editor.Commands
{
@@ -398,5 +399,107 @@ namespace UnityMCP.Editor.Commands
light.shadows = LightShadows.Soft;
return obj;
}
+
+ ///
+ /// Executes a context menu method on a component of a game object
+ ///
+ public static object ExecuteContextMenuItem(JObject @params)
+ {
+ string objectName = (string)@params["object_name"] ?? throw new Exception("Parameter 'object_name' is required.");
+ string componentName = (string)@params["component"] ?? throw new Exception("Parameter 'component' is required.");
+ string contextMenuItemName = (string)@params["context_menu_item"] ?? throw new Exception("Parameter 'context_menu_item' is required.");
+
+ // Find the game object
+ var obj = GameObject.Find(objectName) ?? throw new Exception($"Object '{objectName}' not found.");
+
+ // Find the component type
+ Type componentType = FindTypeInLoadedAssemblies(componentName) ??
+ throw new Exception($"Component type '{componentName}' not found.");
+
+ // Get the component from the game object
+ var component = obj.GetComponent(componentType) ??
+ throw new Exception($"Component '{componentName}' not found on object '{objectName}'.");
+
+ // Find methods with ContextMenu attribute matching the context menu item name
+ var methods = componentType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
+ .Where(m => m.GetCustomAttributes(typeof(ContextMenuItemAttribute), true).Any() ||
+ m.GetCustomAttributes(typeof(ContextMenu), true)
+ .Cast()
+ .Any(attr => attr.menuItem == contextMenuItemName))
+ .ToList();
+
+ // If no methods with ContextMenuItemAttribute are found, look for methods with name matching the context menu item
+ if (methods.Count == 0)
+ {
+ methods = componentType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
+ .Where(m => m.Name == contextMenuItemName)
+ .ToList();
+ }
+
+ if (methods.Count == 0)
+ throw new Exception($"No context menu method '{contextMenuItemName}' found on component '{componentName}'.");
+
+ // If multiple methods match, use the first one and log a warning
+ if (methods.Count > 1)
+ {
+ Debug.LogWarning($"Found multiple methods for context menu item '{contextMenuItemName}' on component '{componentName}'. Using the first one.");
+ }
+
+ var method = methods[0];
+
+ // Execute the method
+ try
+ {
+ method.Invoke(component, null);
+ return new
+ {
+ success = true,
+ message = $"Successfully executed context menu item '{contextMenuItemName}' on component '{componentName}' of object '{objectName}'."
+ };
+ }
+ catch (Exception ex)
+ {
+ throw new Exception($"Error executing context menu item: {ex.Message}");
+ }
+ }
+
+ // Add this helper method to find types across all loaded assemblies
+ private static Type FindTypeInLoadedAssemblies(string typeName)
+ {
+ // First try standard approach
+ Type type = Type.GetType(typeName);
+ if (type != null)
+ return type;
+
+ type = Type.GetType($"UnityEngine.{typeName}");
+ if (type != null)
+ return type;
+
+ // Then search all loaded assemblies
+ foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
+ {
+ // Try with the simple name
+ type = assembly.GetType(typeName);
+ if (type != null)
+ return type;
+
+ // Try with the fully qualified name (assembly.GetTypes() can be expensive, so we do this last)
+ var types = assembly.GetTypes().Where(t => t.Name == typeName).ToArray();
+
+ if (types.Length > 0)
+ {
+ // If we found multiple types with the same name, log a warning
+ if (types.Length > 1)
+ {
+ Debug.LogWarning(
+ $"Found multiple types named '{typeName}'. Using the first one: {types[0].FullName}"
+ );
+ }
+ return types[0];
+ }
+ }
+
+ return null;
+ }
}
}
\ No newline at end of file
diff --git a/Editor/UnityMCPBridge.cs b/Editor/UnityMCPBridge.cs
index 3d4c600..4b36e81 100644
--- a/Editor/UnityMCPBridge.cs
+++ b/Editor/UnityMCPBridge.cs
@@ -286,6 +286,7 @@ namespace UnityMCP.Editor
"CREATE_OBJECT" => ObjectCommandHandler.CreateObject(command.@params),
"MODIFY_OBJECT" => ObjectCommandHandler.ModifyObject(command.@params),
"DELETE_OBJECT" => ObjectCommandHandler.DeleteObject(command.@params),
+ "EXECUTE_CONTEXT_MENU_ITEM" => ObjectCommandHandler.ExecuteContextMenuItem(command.@params),
"GET_OBJECT_PROPERTIES" => ObjectCommandHandler.GetObjectProperties(command.@params),
"GET_COMPONENT_PROPERTIES" => ObjectCommandHandler.GetComponentProperties(command.@params),
"FIND_OBJECTS_BY_NAME" => ObjectCommandHandler.FindObjectsByName(command.@params),
diff --git a/Python/tools/object_tools.py b/Python/tools/object_tools.py
index 293ebef..99534f2 100644
--- a/Python/tools/object_tools.py
+++ b/Python/tools/object_tools.py
@@ -194,4 +194,57 @@ def register_object_tools(mcp: FastMCP):
})
return response.get("assets", [])
except Exception as e:
- return [{"error": f"Failed to get asset list: {str(e)}"}]
\ No newline at end of file
+ return [{"error": f"Failed to get asset list: {str(e)}"}]
+
+ @mcp.tool()
+ def execute_context_menu_item(
+ ctx: Context,
+ object_name: str,
+ component: str,
+ context_menu_item: str
+ ) -> Dict[str, Any]:
+ """Execute a specific [ContextMenu] method on a component of a given game object.
+
+ Args:
+ ctx: The MCP context
+ object_name: Name of the game object to call
+ component: Name of the component type
+ context_menu_item: Name of the context menu item to execute
+
+ Returns:
+ Dict containing the result of the operation
+ """
+ try:
+ unity = get_unity_connection()
+
+ # Check if the object exists
+ found_objects = unity.send_command("FIND_OBJECTS_BY_NAME", {
+ "name": object_name
+ }).get("objects", [])
+
+ if not found_objects:
+ return {"error": f"Object with name '{object_name}' not found in the scene."}
+
+ # Check if the component exists on the object
+ object_props = unity.send_command("GET_OBJECT_PROPERTIES", {
+ "name": object_name
+ })
+
+ if "error" in object_props:
+ return {"error": f"Failed to get object properties: {object_props['error']}"}
+
+ components = object_props.get("components", [])
+ component_exists = any(comp.get("type") == component for comp in components)
+
+ if not component_exists:
+ return {"error": f"Component '{component}' is not attached to object '{object_name}'."}
+
+ # Now execute the context menu item
+ response = unity.send_command("EXECUTE_CONTEXT_MENU_ITEM", {
+ "object_name": object_name,
+ "component": component,
+ "context_menu_item": context_menu_item
+ })
+ return response
+ except Exception as e:
+ return {"error": f"Failed to execute context menu item: {str(e)}"}
\ No newline at end of file