commit
4aff62fe5c
|
|
@ -32,6 +32,7 @@ namespace UnityMCP.Editor.Commands
|
||||||
"BUILD" => HandleBuild(commandParams),
|
"BUILD" => HandleBuild(commandParams),
|
||||||
"EXECUTE_COMMAND" => HandleExecuteCommand(commandParams),
|
"EXECUTE_COMMAND" => HandleExecuteCommand(commandParams),
|
||||||
"READ_CONSOLE" => ReadConsole(commandParams),
|
"READ_CONSOLE" => ReadConsole(commandParams),
|
||||||
|
"GET_AVAILABLE_COMMANDS" => GetAvailableCommands(),
|
||||||
_ => new { error = $"Unknown editor control command: {command}" },
|
_ => new { error = $"Unknown editor control command: {command}" },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -583,5 +584,367 @@ namespace UnityMCP.Editor.Commands
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a comprehensive list of available Unity commands, including editor menu items,
|
||||||
|
/// internal commands, utility methods, and other actionable operations that can be executed.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Object containing categorized lists of available command paths</returns>
|
||||||
|
private static object GetAvailableCommands()
|
||||||
|
{
|
||||||
|
var menuCommands = new HashSet<string>();
|
||||||
|
var utilityCommands = new HashSet<string>();
|
||||||
|
var assetCommands = new HashSet<string>();
|
||||||
|
var sceneCommands = new HashSet<string>();
|
||||||
|
var gameObjectCommands = new HashSet<string>();
|
||||||
|
var prefabCommands = new HashSet<string>();
|
||||||
|
var shortcutCommands = new HashSet<string>();
|
||||||
|
var otherCommands = new HashSet<string>();
|
||||||
|
|
||||||
|
// Add a simple command that we know will work for testing
|
||||||
|
menuCommands.Add("Window/Unity MCP");
|
||||||
|
|
||||||
|
Debug.Log("Starting command collection...");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Add all EditorApplication static methods - these are guaranteed to work
|
||||||
|
Debug.Log("Adding EditorApplication methods...");
|
||||||
|
foreach (MethodInfo method in typeof(EditorApplication).GetMethods(BindingFlags.Public | BindingFlags.Static))
|
||||||
|
{
|
||||||
|
utilityCommands.Add($"EditorApplication.{method.Name}");
|
||||||
|
}
|
||||||
|
Debug.Log($"Added {utilityCommands.Count} EditorApplication methods");
|
||||||
|
|
||||||
|
// Add built-in menu commands directly - these are common ones that should always be available
|
||||||
|
Debug.Log("Adding built-in menu commands...");
|
||||||
|
string[] builtInMenus = new[] {
|
||||||
|
"File/New Scene",
|
||||||
|
"File/Open Scene",
|
||||||
|
"File/Save",
|
||||||
|
"File/Save As...",
|
||||||
|
"Edit/Undo",
|
||||||
|
"Edit/Redo",
|
||||||
|
"Edit/Cut",
|
||||||
|
"Edit/Copy",
|
||||||
|
"Edit/Paste",
|
||||||
|
"Edit/Duplicate",
|
||||||
|
"Edit/Delete",
|
||||||
|
"GameObject/Create Empty",
|
||||||
|
"GameObject/3D Object/Cube",
|
||||||
|
"GameObject/3D Object/Sphere",
|
||||||
|
"GameObject/3D Object/Capsule",
|
||||||
|
"GameObject/3D Object/Cylinder",
|
||||||
|
"GameObject/3D Object/Plane",
|
||||||
|
"GameObject/Light/Directional Light",
|
||||||
|
"GameObject/Light/Point Light",
|
||||||
|
"GameObject/Light/Spotlight",
|
||||||
|
"GameObject/Light/Area Light",
|
||||||
|
"Component/Mesh/Mesh Filter",
|
||||||
|
"Component/Mesh/Mesh Renderer",
|
||||||
|
"Component/Physics/Rigidbody",
|
||||||
|
"Component/Physics/Box Collider",
|
||||||
|
"Component/Physics/Sphere Collider",
|
||||||
|
"Component/Physics/Capsule Collider",
|
||||||
|
"Component/Audio/Audio Source",
|
||||||
|
"Component/Audio/Audio Listener",
|
||||||
|
"Window/General/Scene",
|
||||||
|
"Window/General/Game",
|
||||||
|
"Window/General/Inspector",
|
||||||
|
"Window/General/Hierarchy",
|
||||||
|
"Window/General/Project",
|
||||||
|
"Window/General/Console",
|
||||||
|
"Window/Analysis/Profiler",
|
||||||
|
"Window/Package Manager",
|
||||||
|
"Assets/Create/Material",
|
||||||
|
"Assets/Create/C# Script",
|
||||||
|
"Assets/Create/Prefab",
|
||||||
|
"Assets/Create/Scene",
|
||||||
|
"Assets/Create/Folder",
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (string menuItem in builtInMenus)
|
||||||
|
{
|
||||||
|
menuCommands.Add(menuItem);
|
||||||
|
}
|
||||||
|
Debug.Log($"Added {builtInMenus.Length} built-in menu commands");
|
||||||
|
|
||||||
|
// Get menu commands from MenuItem attributes - wrapped in separate try block
|
||||||
|
Debug.Log("Searching for MenuItem attributes...");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int itemCount = 0;
|
||||||
|
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
|
||||||
|
{
|
||||||
|
if (assembly.IsDynamic) continue;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
foreach (Type type in assembly.GetExportedTypes())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
foreach (MethodInfo method in type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
object[] attributes = method.GetCustomAttributes(typeof(UnityEditor.MenuItem), false);
|
||||||
|
if (attributes != null && attributes.Length > 0)
|
||||||
|
{
|
||||||
|
foreach (var attr in attributes)
|
||||||
|
{
|
||||||
|
var menuItem = attr as UnityEditor.MenuItem;
|
||||||
|
if (menuItem != null && !string.IsNullOrEmpty(menuItem.menuItem))
|
||||||
|
{
|
||||||
|
menuCommands.Add(menuItem.menuItem);
|
||||||
|
itemCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception methodEx)
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"Error getting menu items for method {method.Name}: {methodEx.Message}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception typeEx)
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"Error processing type: {typeEx.Message}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception assemblyEx)
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"Error examining assembly {assembly.GetName().Name}: {assemblyEx.Message}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Debug.Log($"Found {itemCount} menu items from attributes");
|
||||||
|
}
|
||||||
|
catch (Exception menuItemEx)
|
||||||
|
{
|
||||||
|
Debug.LogError($"Failed to get menu items: {menuItemEx.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add EditorUtility methods as commands
|
||||||
|
Debug.Log("Adding EditorUtility methods...");
|
||||||
|
foreach (MethodInfo method in typeof(EditorUtility).GetMethods(BindingFlags.Public | BindingFlags.Static))
|
||||||
|
{
|
||||||
|
utilityCommands.Add($"EditorUtility.{method.Name}");
|
||||||
|
}
|
||||||
|
Debug.Log($"Added {typeof(EditorUtility).GetMethods(BindingFlags.Public | BindingFlags.Static).Length} EditorUtility methods");
|
||||||
|
|
||||||
|
// Add AssetDatabase methods as commands
|
||||||
|
Debug.Log("Adding AssetDatabase methods...");
|
||||||
|
foreach (MethodInfo method in typeof(AssetDatabase).GetMethods(BindingFlags.Public | BindingFlags.Static))
|
||||||
|
{
|
||||||
|
assetCommands.Add($"AssetDatabase.{method.Name}");
|
||||||
|
}
|
||||||
|
Debug.Log($"Added {typeof(AssetDatabase).GetMethods(BindingFlags.Public | BindingFlags.Static).Length} AssetDatabase methods");
|
||||||
|
|
||||||
|
// Add EditorSceneManager methods as commands
|
||||||
|
Debug.Log("Adding EditorSceneManager methods...");
|
||||||
|
Type sceneManagerType = typeof(UnityEditor.SceneManagement.EditorSceneManager);
|
||||||
|
if (sceneManagerType != null)
|
||||||
|
{
|
||||||
|
foreach (MethodInfo method in sceneManagerType.GetMethods(BindingFlags.Public | BindingFlags.Static))
|
||||||
|
{
|
||||||
|
sceneCommands.Add($"EditorSceneManager.{method.Name}");
|
||||||
|
}
|
||||||
|
Debug.Log($"Added {sceneManagerType.GetMethods(BindingFlags.Public | BindingFlags.Static).Length} EditorSceneManager methods");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add GameObject manipulation commands
|
||||||
|
Debug.Log("Adding GameObject methods...");
|
||||||
|
foreach (MethodInfo method in typeof(GameObject).GetMethods(BindingFlags.Public | BindingFlags.Static))
|
||||||
|
{
|
||||||
|
gameObjectCommands.Add($"GameObject.{method.Name}");
|
||||||
|
}
|
||||||
|
Debug.Log($"Added {typeof(GameObject).GetMethods(BindingFlags.Public | BindingFlags.Static).Length} GameObject methods");
|
||||||
|
|
||||||
|
// Add Selection-related commands
|
||||||
|
Debug.Log("Adding Selection methods...");
|
||||||
|
foreach (MethodInfo method in typeof(Selection).GetMethods(BindingFlags.Public | BindingFlags.Static))
|
||||||
|
{
|
||||||
|
gameObjectCommands.Add($"Selection.{method.Name}");
|
||||||
|
}
|
||||||
|
Debug.Log($"Added {typeof(Selection).GetMethods(BindingFlags.Public | BindingFlags.Static).Length} Selection methods");
|
||||||
|
|
||||||
|
// Add PrefabUtility methods as commands
|
||||||
|
Debug.Log("Adding PrefabUtility methods...");
|
||||||
|
Type prefabUtilityType = typeof(UnityEditor.PrefabUtility);
|
||||||
|
if (prefabUtilityType != null)
|
||||||
|
{
|
||||||
|
foreach (MethodInfo method in prefabUtilityType.GetMethods(BindingFlags.Public | BindingFlags.Static))
|
||||||
|
{
|
||||||
|
prefabCommands.Add($"PrefabUtility.{method.Name}");
|
||||||
|
}
|
||||||
|
Debug.Log($"Added {prefabUtilityType.GetMethods(BindingFlags.Public | BindingFlags.Static).Length} PrefabUtility methods");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add Undo related methods
|
||||||
|
Debug.Log("Adding Undo methods...");
|
||||||
|
foreach (MethodInfo method in typeof(Undo).GetMethods(BindingFlags.Public | BindingFlags.Static))
|
||||||
|
{
|
||||||
|
utilityCommands.Add($"Undo.{method.Name}");
|
||||||
|
}
|
||||||
|
Debug.Log($"Added {typeof(Undo).GetMethods(BindingFlags.Public | BindingFlags.Static).Length} Undo methods");
|
||||||
|
|
||||||
|
// The rest of the command gathering can be attempted but might not be critical
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Get commands from Unity's internal command system
|
||||||
|
Debug.Log("Trying to get internal CommandService commands...");
|
||||||
|
Type commandServiceType = typeof(UnityEditor.EditorWindow).Assembly.GetType("UnityEditor.CommandService");
|
||||||
|
if (commandServiceType != null)
|
||||||
|
{
|
||||||
|
Debug.Log("Found CommandService type");
|
||||||
|
PropertyInfo instanceProperty = commandServiceType.GetProperty("Instance",
|
||||||
|
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
|
||||||
|
|
||||||
|
if (instanceProperty != null)
|
||||||
|
{
|
||||||
|
Debug.Log("Found Instance property");
|
||||||
|
object commandService = instanceProperty.GetValue(null);
|
||||||
|
if (commandService != null)
|
||||||
|
{
|
||||||
|
Debug.Log("Got CommandService instance");
|
||||||
|
MethodInfo findAllCommandsMethod = commandServiceType.GetMethod("FindAllCommands",
|
||||||
|
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
|
||||||
|
|
||||||
|
if (findAllCommandsMethod != null)
|
||||||
|
{
|
||||||
|
Debug.Log("Found FindAllCommands method");
|
||||||
|
var commandsResult = findAllCommandsMethod.Invoke(commandService, null);
|
||||||
|
if (commandsResult != null)
|
||||||
|
{
|
||||||
|
Debug.Log("Got commands result");
|
||||||
|
var commandsList = commandsResult as System.Collections.IEnumerable;
|
||||||
|
if (commandsList != null)
|
||||||
|
{
|
||||||
|
int commandCount = 0;
|
||||||
|
foreach (var cmd in commandsList)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
PropertyInfo nameProperty = cmd.GetType().GetProperty("name") ??
|
||||||
|
cmd.GetType().GetProperty("path") ??
|
||||||
|
cmd.GetType().GetProperty("commandName");
|
||||||
|
if (nameProperty != null)
|
||||||
|
{
|
||||||
|
string commandName = nameProperty.GetValue(cmd)?.ToString();
|
||||||
|
if (!string.IsNullOrEmpty(commandName))
|
||||||
|
{
|
||||||
|
otherCommands.Add(commandName);
|
||||||
|
commandCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception cmdEx)
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"Error processing command: {cmdEx.Message}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Debug.Log($"Added {commandCount} internal commands");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.LogWarning("FindAllCommands returned null");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.LogWarning("FindAllCommands method not found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.LogWarning("CommandService instance is null");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.LogWarning("Instance property not found on CommandService");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.LogWarning("CommandService type not found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"Failed to get internal Unity commands: {e.Message}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Other additional command sources can be tried
|
||||||
|
// ... other commands ...
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Debug.LogError($"Error getting Unity commands: {e.Message}\n{e.StackTrace}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create command categories dictionary for the result
|
||||||
|
var commandCategories = new Dictionary<string, List<string>>
|
||||||
|
{
|
||||||
|
{ "MenuCommands", menuCommands.OrderBy(x => x).ToList() },
|
||||||
|
{ "UtilityCommands", utilityCommands.OrderBy(x => x).ToList() },
|
||||||
|
{ "AssetCommands", assetCommands.OrderBy(x => x).ToList() },
|
||||||
|
{ "SceneCommands", sceneCommands.OrderBy(x => x).ToList() },
|
||||||
|
{ "GameObjectCommands", gameObjectCommands.OrderBy(x => x).ToList() },
|
||||||
|
{ "PrefabCommands", prefabCommands.OrderBy(x => x).ToList() },
|
||||||
|
{ "ShortcutCommands", shortcutCommands.OrderBy(x => x).ToList() },
|
||||||
|
{ "OtherCommands", otherCommands.OrderBy(x => x).ToList() }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Calculate total command count
|
||||||
|
int totalCount = commandCategories.Values.Sum(list => list.Count);
|
||||||
|
|
||||||
|
Debug.Log($"Command retrieval complete. Found {totalCount} total commands.");
|
||||||
|
|
||||||
|
// Create a simplified response with just the essential data
|
||||||
|
// The complex object structure might be causing serialization issues
|
||||||
|
var allCommandsList = commandCategories.Values.SelectMany(x => x).OrderBy(x => x).ToList();
|
||||||
|
|
||||||
|
// Use simple string array instead of JArray for better serialization
|
||||||
|
string[] commandsArray = allCommandsList.ToArray();
|
||||||
|
|
||||||
|
// Log the array size for verification
|
||||||
|
Debug.Log($"Final commands array contains {commandsArray.Length} items");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Return a simple object with just the commands array and count
|
||||||
|
var result = new
|
||||||
|
{
|
||||||
|
commands = commandsArray,
|
||||||
|
count = commandsArray.Length
|
||||||
|
};
|
||||||
|
|
||||||
|
// Verify the result can be serialized properly
|
||||||
|
var jsonTest = JsonUtility.ToJson(new { test = "This is a test" });
|
||||||
|
Debug.Log($"JSON serialization test successful: {jsonTest}");
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Debug.LogError($"Error creating response: {ex.Message}");
|
||||||
|
|
||||||
|
// Ultimate fallback - don't use any JObject/JArray
|
||||||
|
return new
|
||||||
|
{
|
||||||
|
message = $"Found {commandsArray.Length} commands",
|
||||||
|
firstTen = commandsArray.Take(10).ToArray(),
|
||||||
|
count = commandsArray.Length
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -7,6 +7,7 @@ using UnityEditor;
|
||||||
using UnityEngine.SceneManagement;
|
using UnityEngine.SceneManagement;
|
||||||
using UnityEditor.SceneManagement;
|
using UnityEditor.SceneManagement;
|
||||||
using UnityMCP.Editor.Helpers;
|
using UnityMCP.Editor.Helpers;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
namespace UnityMCP.Editor.Commands
|
namespace UnityMCP.Editor.Commands
|
||||||
{
|
{
|
||||||
|
|
@ -398,5 +399,107 @@ namespace UnityMCP.Editor.Commands
|
||||||
light.shadows = LightShadows.Soft;
|
light.shadows = LightShadows.Soft;
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Executes a context menu method on a component of a game object
|
||||||
|
/// </summary>
|
||||||
|
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<ContextMenu>()
|
||||||
|
.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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -286,6 +286,7 @@ namespace UnityMCP.Editor
|
||||||
"CREATE_OBJECT" => ObjectCommandHandler.CreateObject(command.@params),
|
"CREATE_OBJECT" => ObjectCommandHandler.CreateObject(command.@params),
|
||||||
"MODIFY_OBJECT" => ObjectCommandHandler.ModifyObject(command.@params),
|
"MODIFY_OBJECT" => ObjectCommandHandler.ModifyObject(command.@params),
|
||||||
"DELETE_OBJECT" => ObjectCommandHandler.DeleteObject(command.@params),
|
"DELETE_OBJECT" => ObjectCommandHandler.DeleteObject(command.@params),
|
||||||
|
"EXECUTE_CONTEXT_MENU_ITEM" => ObjectCommandHandler.ExecuteContextMenuItem(command.@params),
|
||||||
"GET_OBJECT_PROPERTIES" => ObjectCommandHandler.GetObjectProperties(command.@params),
|
"GET_OBJECT_PROPERTIES" => ObjectCommandHandler.GetObjectProperties(command.@params),
|
||||||
"GET_COMPONENT_PROPERTIES" => ObjectCommandHandler.GetComponentProperties(command.@params),
|
"GET_COMPONENT_PROPERTIES" => ObjectCommandHandler.GetComponentProperties(command.@params),
|
||||||
"FIND_OBJECTS_BY_NAME" => ObjectCommandHandler.FindObjectsByName(command.@params),
|
"FIND_OBJECTS_BY_NAME" => ObjectCommandHandler.FindObjectsByName(command.@params),
|
||||||
|
|
|
||||||
|
|
@ -267,3 +267,29 @@ def register_editor_tools(mcp: FastMCP):
|
||||||
"message": f"Error reading console: {str(e)}",
|
"message": f"Error reading console: {str(e)}",
|
||||||
"stackTrace": ""
|
"stackTrace": ""
|
||||||
}]
|
}]
|
||||||
|
|
||||||
|
@mcp.tool()
|
||||||
|
def get_available_commands(ctx: Context) -> List[str]:
|
||||||
|
"""Get a list of all available editor commands that can be executed.
|
||||||
|
|
||||||
|
This tool provides direct access to the list of commands that can be executed
|
||||||
|
in the Unity Editor through the MCP system.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List[str]: List of available command paths
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
unity = get_unity_connection()
|
||||||
|
|
||||||
|
# Send request for available commands
|
||||||
|
response = unity.send_command("EDITOR_CONTROL", {
|
||||||
|
"command": "GET_AVAILABLE_COMMANDS"
|
||||||
|
})
|
||||||
|
|
||||||
|
# Extract commands list
|
||||||
|
commands = response.get("commands", [])
|
||||||
|
|
||||||
|
# Return the commands list
|
||||||
|
return commands
|
||||||
|
except Exception as e:
|
||||||
|
return [f"Error fetching commands: {str(e)}"]
|
||||||
|
|
@ -195,3 +195,56 @@ def register_object_tools(mcp: FastMCP):
|
||||||
return response.get("assets", [])
|
return response.get("assets", [])
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return [{"error": f"Failed to get asset list: {str(e)}"}]
|
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)}"}
|
||||||
Loading…
Reference in New Issue