Skip to content

Improve Command System: Added Command Pattern and Editor Integration #110

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
58 changes: 49 additions & 9 deletions UnityMcpBridge/Editor/Tools/CommandRegistry.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using Newtonsoft.Json.Linq;
using UnityEngine;

namespace UnityMcpBridge.Editor.Tools
{
Expand All @@ -11,16 +12,55 @@ public static class CommandRegistry
{
// Maps command names (matching those called from Python via ctx.bridge.unity_editor.HandlerName)
// to the corresponding static HandleCommand method in the appropriate tool class.
private static readonly Dictionary<string, Func<JObject, object>> _handlers = new()
private static readonly Dictionary<string, Func<JObject, object>> _handlers = new();


/// <summary>
/// Registers a command handler with a specified name.
/// </summary>
/// <param name="name">The name of the command to register.</param>
/// <param name="commandHandler">The function to handle the command.</param>
public static void RegisterCommand(string name, Func<JObject, object> commandHandler)
{
// Use case-insensitive comparison for flexibility, although Python side should be consistent
if (!_handlers.ContainsKey(name))
{
_handlers.Add(name.ToLower(), commandHandler);

Debug.Log($"Command '{name}' registered.");
}
}


/// <summary>
/// Executes a registered command by name with the provided parameters.
/// </summary>
/// <param name="name">The name of the command to execute.</param>
/// <param name="paramsObject">The parameters to pass to the command handler.</param>
/// <returns>The result of the command execution or an error message if the command is not found.</returns>
public static object ExecuteCommand(string name, JObject paramsObject)
{
if (_handlers.TryGetValue(name.ToLower(), out var command))
{
return command(paramsObject);
}
else
{
Debug.Log($"Command '{name}' not found.");
return $"Command '{name}' not found.";
}
}


/// <summary>
/// Retrieves a list of all registered command names.
/// </summary>
/// <returns>A list of registered command names.</returns>
public static List<string> GetRegisteredCommands()
{
{ "HandleManageScript", ManageScript.HandleCommand },
{ "HandleManageScene", ManageScene.HandleCommand },
{ "HandleManageEditor", ManageEditor.HandleCommand },
{ "HandleManageGameObject", ManageGameObject.HandleCommand },
{ "HandleManageAsset", ManageAsset.HandleCommand },
{ "HandleReadConsole", ReadConsole.HandleCommand },
{ "HandleExecuteMenuItem", ExecuteMenuItem.HandleCommand },
};
// Return a list of registered command names
return new List<string>(_handlers.Keys);
}

/// <summary>
/// Gets a command handler by name.
Expand Down
6 changes: 6 additions & 0 deletions UnityMcpBridge/Editor/Tools/ExecuteMenuItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,14 @@ namespace UnityMcpBridge.Editor.Tools
/// <summary>
/// Handles executing Unity Editor menu items by path.
/// </summary>
[InitializeOnLoad]
public static class ExecuteMenuItem
{
static ExecuteMenuItem()
{
CommandRegistry.RegisterCommand("execute_menu_item", HandleCommand);
}

// Basic blacklist to prevent accidental execution of potentially disruptive menu items.
// This can be expanded based on needs.
private static readonly HashSet<string> _menuPathBlacklist = new HashSet<string>(
Expand Down
6 changes: 6 additions & 0 deletions UnityMcpBridge/Editor/Tools/ManageAsset.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,16 @@ namespace UnityMcpBridge.Editor.Tools
/// <summary>
/// Handles asset management operations within the Unity project.
/// </summary>
[InitializeOnLoad]
public static class ManageAsset
{
// --- Main Handler ---

static ManageAsset()
{
CommandRegistry.RegisterCommand("manage_asset", HandleCommand);
}

// Define the list of valid actions
private static readonly List<string> ValidActions = new List<string>
{
Expand Down
6 changes: 6 additions & 0 deletions UnityMcpBridge/Editor/Tools/ManageEditor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,14 @@ namespace UnityMcpBridge.Editor.Tools
/// Handles operations related to controlling and querying the Unity Editor state,
/// including managing Tags and Layers.
/// </summary>
[InitializeOnLoad]
public static class ManageEditor
{
static ManageEditor()
{
CommandRegistry.RegisterCommand("manage_editor", HandleCommand);
}

// Constant for starting user layer index
private const int FirstUserLayerIndex = 8;

Expand Down
6 changes: 6 additions & 0 deletions UnityMcpBridge/Editor/Tools/ManageGameObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,16 @@ namespace UnityMcpBridge.Editor.Tools
/// <summary>
/// Handles GameObject manipulation within the current scene (CRUD, find, components).
/// </summary>
[InitializeOnLoad]
public static class ManageGameObject
{
// --- Main Handler ---

static ManageGameObject()
{
CommandRegistry.RegisterCommand("manage_gameobject", HandleCommand);
}

public static object HandleCommand(JObject @params)
{
string action = @params["action"]?.ToString().ToLower();
Expand Down
7 changes: 7 additions & 0 deletions UnityMcpBridge/Editor/Tools/ManageScene.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,15 @@ namespace UnityMcpBridge.Editor.Tools
/// <summary>
/// Handles scene management operations like loading, saving, creating, and querying hierarchy.
/// </summary>
[InitializeOnLoad]

public static class ManageScene
{
static ManageScene()
{
CommandRegistry.RegisterCommand("manage_scene", HandleCommand);
}

/// <summary>
/// Main handler for scene management actions.
/// </summary>
Expand Down
6 changes: 6 additions & 0 deletions UnityMcpBridge/Editor/Tools/ManageScript.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,14 @@ namespace UnityMcpBridge.Editor.Tools
/// <summary>
/// Handles CRUD operations for C# scripts within the Unity project.
/// </summary>
[InitializeOnLoad]
public static class ManageScript
{
static ManageScript()
{
CommandRegistry.RegisterCommand("manage_script", HandleCommand);
}

/// <summary>
/// Main handler for script management actions.
/// </summary>
Expand Down
3 changes: 3 additions & 0 deletions UnityMcpBridge/Editor/Tools/ReadConsole.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ namespace UnityMcpBridge.Editor.Tools
/// Handles reading and clearing Unity Editor console log entries.
/// Uses reflection to access internal LogEntry methods/properties.
/// </summary>
[InitializeOnLoad]
public static class ReadConsole
{
// Reflection members for accessing internal LogEntry data
Expand All @@ -34,6 +35,8 @@ public static class ReadConsole
// Static constructor for reflection setup
static ReadConsole()
{
CommandRegistry.RegisterCommand("read_console", HandleCommand);

try
{
Type logEntriesType = typeof(EditorApplication).Assembly.GetType(
Expand Down
18 changes: 2 additions & 16 deletions UnityMcpBridge/Editor/UnityMcpBridge.cs
Original file line number Diff line number Diff line change
Expand Up @@ -368,22 +368,8 @@ private static string ExecuteCommand(Command command)
// Use JObject for parameters as the new handlers likely expect this
JObject paramsObject = command.@params ?? new JObject();

// Route command based on the new tool structure from the refactor plan
object result = command.type switch
{
// Maps the command type (tool name) to the corresponding handler's static HandleCommand method
// Assumes each handler class has a static method named 'HandleCommand' that takes JObject parameters
"manage_script" => ManageScript.HandleCommand(paramsObject),
"manage_scene" => ManageScene.HandleCommand(paramsObject),
"manage_editor" => ManageEditor.HandleCommand(paramsObject),
"manage_gameobject" => ManageGameObject.HandleCommand(paramsObject),
"manage_asset" => ManageAsset.HandleCommand(paramsObject),
"read_console" => ReadConsole.HandleCommand(paramsObject),
"execute_menu_item" => ExecuteMenuItem.HandleCommand(paramsObject),
_ => throw new ArgumentException(
$"Unknown or unsupported command type: {command.type}"
),
};
// Using command registry to execute command
var result = CommandRegistry.ExecuteCommand(command.type, paramsObject);

// Standard success response format
var response = new { status = "success", result };
Expand Down
29 changes: 26 additions & 3 deletions UnityMcpBridge/Editor/Windows/UnityMcpEditorWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using UnityMcpBridge.Editor.Data;
using UnityMcpBridge.Editor.Helpers;
using UnityMcpBridge.Editor.Models;
using UnityMcpBridge.Editor.Tools;

namespace UnityMcpBridge.Editor.Windows
{
Expand All @@ -20,6 +21,8 @@ public class UnityMcpEditorWindow : EditorWindow
private const int mcpPort = 6500; // Hardcoded MCP port
private readonly McpClients mcpClients = new();

private bool mpcClientConfigFolder;

[MenuItem("Window/Unity MCP")]
public static void ShowWindow()
{
Expand Down Expand Up @@ -251,10 +254,30 @@ private void OnGUI()
}
EditorGUILayout.EndVertical();

foreach (McpClient mcpClient in mcpClients.clients)
EditorGUILayout.Space(10);

// Registered commands list section
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
EditorGUILayout.LabelField("Registered commands", EditorStyles.boldLabel);
int counter = 1;
foreach (var command in CommandRegistry.GetRegisteredCommands())
{
EditorGUILayout.LabelField($"[{counter++}] {command}");
}
EditorGUILayout.EndVertical();

EditorGUILayout.Space(10);

// MCP client configuration sections
mpcClientConfigFolder = EditorGUILayout.Foldout(mpcClientConfigFolder, "MCP Client Configuration");

if (mpcClientConfigFolder)
{
EditorGUILayout.Space(10);
ConfigurationSection(mcpClient);
foreach (McpClient mcpClient in mcpClients.clients)
{
EditorGUILayout.Space(10);
ConfigurationSection(mcpClient);
}
}

EditorGUILayout.EndScrollView();
Expand Down