unity-mcp/UnityMcpBridge/Editor/Tools/ExecuteMenuItem.cs

131 lines
6.0 KiB
C#
Raw Normal View History

2025-03-31 03:58:01 +08:00
using System;
using System.Collections.Generic; // Added for HashSet
using Newtonsoft.Json.Linq;
using UnityEditor;
using UnityEngine;
Rename namespace and public facing plugin output from "Unity MCP" to "MCP for Unity" (#225) * refactor: rename namespace from UnityMcpBridge to MCPForUnity across all files See thread in #6, we can't use Unity MCP because it violates their trademark. That name makes us look affiliated. We can use MCP for Unity * Change package display name, menu item and menu titles These are front facing so has to change for Unity asset store review * Misc name changes in logs and comments for better consistency * chore: update editor window title from 'MCP Editor' to 'MCP for Unity' * refactor: update branding from UNITY-MCP to MCP-FOR-UNITY across all log messages and warnings * chore: rename Unity MCP to MCP For Unity across all files and bump version to 2.1.2 * docs: update restore script title to clarify Unity MCP naming * Fix usage instructions * chore: update log messages to use MCP For Unity branding instead of UnityMCP * Add a README inside plugin, required for distributing via the asset store * docs: update Unity port description and fix typo in troubleshooting section * Address Rabbit feedback * Update Editor prefs to use new name Prevents overlap with other Unity MCPs, happy to revert if it's too much * refactor: rename server logger and identifier from unity-mcp-server to mcp-for-unity-server * Standardize casing of renamed project to "MCP for Unity", as it is on the asset store * Remove unused folder * refactor: rename Unity MCP to MCP for Unity across codebase * Update dangling references * docs: update product name from UnityMCP to MCP for Unity in README * Update log and comments for new name
2025-08-21 03:59:49 +08:00
using MCPForUnity.Editor.Helpers; // For Response class
2025-03-31 03:58:01 +08:00
Rename namespace and public facing plugin output from "Unity MCP" to "MCP for Unity" (#225) * refactor: rename namespace from UnityMcpBridge to MCPForUnity across all files See thread in #6, we can't use Unity MCP because it violates their trademark. That name makes us look affiliated. We can use MCP for Unity * Change package display name, menu item and menu titles These are front facing so has to change for Unity asset store review * Misc name changes in logs and comments for better consistency * chore: update editor window title from 'MCP Editor' to 'MCP for Unity' * refactor: update branding from UNITY-MCP to MCP-FOR-UNITY across all log messages and warnings * chore: rename Unity MCP to MCP For Unity across all files and bump version to 2.1.2 * docs: update restore script title to clarify Unity MCP naming * Fix usage instructions * chore: update log messages to use MCP For Unity branding instead of UnityMCP * Add a README inside plugin, required for distributing via the asset store * docs: update Unity port description and fix typo in troubleshooting section * Address Rabbit feedback * Update Editor prefs to use new name Prevents overlap with other Unity MCPs, happy to revert if it's too much * refactor: rename server logger and identifier from unity-mcp-server to mcp-for-unity-server * Standardize casing of renamed project to "MCP for Unity", as it is on the asset store * Remove unused folder * refactor: rename Unity MCP to MCP for Unity across codebase * Update dangling references * docs: update product name from UnityMCP to MCP for Unity in README * Update log and comments for new name
2025-08-21 03:59:49 +08:00
namespace MCPForUnity.Editor.Tools
2025-03-31 03:58:01 +08:00
{
/// <summary>
/// Handles executing Unity Editor menu items by path.
/// </summary>
public static class ExecuteMenuItem
{
// 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>(
StringComparer.OrdinalIgnoreCase
)
2025-03-31 03:58:01 +08:00
{
"File/Quit",
// Add other potentially dangerous items like "Edit/Preferences...", "File/Build Settings..." if needed
};
/// <summary>
/// Main handler for executing menu items or getting available ones.
/// </summary>
public static object HandleCommand(JObject @params)
{
string action = (@params["action"]?.ToString())?.ToLowerInvariant() ?? "execute"; // Default action
2025-03-31 03:58:01 +08:00
try
{
switch (action)
{
case "execute":
return ExecuteItem(@params);
case "get_available_menus":
// Getting a comprehensive list of *all* menu items dynamically is very difficult
// and often requires complex reflection or maintaining a manual list.
// Returning a placeholder/acknowledgement for now.
Debug.LogWarning(
"[ExecuteMenuItem] 'get_available_menus' action is not fully implemented. Dynamically listing all menu items is complex."
);
2025-03-31 03:58:01 +08:00
// Returning an empty list as per the refactor plan's requirements.
return Response.Success(
"'get_available_menus' action is not fully implemented. Returning empty list.",
new List<string>()
);
// TODO: Consider implementing a basic list of common/known menu items or exploring reflection techniques if this feature becomes critical.
2025-03-31 03:58:01 +08:00
default:
return Response.Error(
$"Unknown action: '{action}'. Valid actions are 'execute', 'get_available_menus'."
);
2025-03-31 03:58:01 +08:00
}
}
catch (Exception e)
{
Debug.LogError($"[ExecuteMenuItem] Action '{action}' failed: {e}");
return Response.Error($"Internal error processing action '{action}': {e.Message}");
}
}
/// <summary>
/// Executes a specific menu item.
/// </summary>
private static object ExecuteItem(JObject @params)
{
// Try both naming conventions: snake_case and camelCase
string menuPath = @params["menu_path"]?.ToString() ?? @params["menuPath"]?.ToString();
feat: Automatic Background Compile and Domain Reload for MCP Script Edits and New Script Creation (#248) * Unity MCP: reliable auto-reload via Unity-side sentinel flip; remove Python writes - Add MCP/Flip Reload Sentinel editor menu and flip package sentinel synchronously - Trigger sentinel flip after Create/Update/ApplyTextEdits (sync) in ManageScript - Instrument flip path with Debug.Log for traceability - Remove Python reload_sentinel writes; tools now execute Unity menu instead - Harden reload_sentinel path resolution to project/package - ExecuteMenuItem runs synchronously for deterministic results - Verified MCP edits trigger compile/reload without focus; no Python permission errors * Getting double flips * Fix double reload and ensure accurate batch edits; immediate structured reloads * Remove MCP/Flip Reload Sentinel menu; rely on synchronous import/compile for reloads * Route bridge/editor logs through McpLog and gate behind debug; create path now reloads synchronously * chore: ignore backup artifacts; remove stray ManageScript.cs.backup files * fix span logic * fix: honor UNITY_MCP_STATUS_DIR for sentinel status file lookup (fallback to ~/.unity-mcp) * test: add sentinel test honoring UNITY_MCP_STATUS_DIR; chore: ManageScript overlap check simplification and log consistency * Harden environment path, remove extraneous flip menu test * refactor: centralize import/compile via ManageScriptRefreshHelpers.ImportAndRequestCompile; replace duplicated sequences * feat: add scheduledRefresh flag; standardize logs; gate info and DRY immediate import/compile * chore: remove execute_menu_item sentinel flip from manage_script_edits; rely on import/compile * chore: remove unused MCP reload sentinel mechanism * fix: honor ignore_case for anchor_insert in text conversion path
2025-09-03 00:36:50 +08:00
// Optional future param retained for API compatibility; not used in synchronous mode
// int timeoutMs = Math.Max(0, (@params["timeout_ms"]?.ToObject<int>() ?? 2000));
2025-03-31 03:58:01 +08:00
// string alias = @params["alias"]?.ToString(); // TODO: Implement alias mapping based on refactor plan requirements.
// JObject parameters = @params["parameters"] as JObject; // TODO: Investigate parameter passing (often not directly supported by ExecuteMenuItem).
if (string.IsNullOrWhiteSpace(menuPath))
{
return Response.Error("Required parameter 'menu_path' or 'menuPath' is missing or empty.");
2025-03-31 03:58:01 +08:00
}
// Validate against blacklist
if (_menuPathBlacklist.Contains(menuPath))
{
return Response.Error(
$"Execution of menu item '{menuPath}' is blocked for safety reasons."
);
2025-03-31 03:58:01 +08:00
}
// TODO: Implement alias lookup here if needed (Map alias to actual menuPath).
// if (!string.IsNullOrEmpty(alias)) { menuPath = LookupAlias(alias); if(menuPath == null) return Response.Error(...); }
// TODO: Handle parameters ('parameters' object) if a viable method is found.
// This is complex as EditorApplication.ExecuteMenuItem doesn't take arguments directly.
// It might require finding the underlying EditorWindow or command if parameters are needed.
try
{
// Trace incoming execute requests (debug-gated)
McpLog.Info($"[ExecuteMenuItem] Request to execute menu: '{menuPath}'", always: false);
2025-03-31 03:58:01 +08:00
feat: Automatic Background Compile and Domain Reload for MCP Script Edits and New Script Creation (#248) * Unity MCP: reliable auto-reload via Unity-side sentinel flip; remove Python writes - Add MCP/Flip Reload Sentinel editor menu and flip package sentinel synchronously - Trigger sentinel flip after Create/Update/ApplyTextEdits (sync) in ManageScript - Instrument flip path with Debug.Log for traceability - Remove Python reload_sentinel writes; tools now execute Unity menu instead - Harden reload_sentinel path resolution to project/package - ExecuteMenuItem runs synchronously for deterministic results - Verified MCP edits trigger compile/reload without focus; no Python permission errors * Getting double flips * Fix double reload and ensure accurate batch edits; immediate structured reloads * Remove MCP/Flip Reload Sentinel menu; rely on synchronous import/compile for reloads * Route bridge/editor logs through McpLog and gate behind debug; create path now reloads synchronously * chore: ignore backup artifacts; remove stray ManageScript.cs.backup files * fix span logic * fix: honor UNITY_MCP_STATUS_DIR for sentinel status file lookup (fallback to ~/.unity-mcp) * test: add sentinel test honoring UNITY_MCP_STATUS_DIR; chore: ManageScript overlap check simplification and log consistency * Harden environment path, remove extraneous flip menu test * refactor: centralize import/compile via ManageScriptRefreshHelpers.ImportAndRequestCompile; replace duplicated sequences * feat: add scheduledRefresh flag; standardize logs; gate info and DRY immediate import/compile * chore: remove execute_menu_item sentinel flip from manage_script_edits; rely on import/compile * chore: remove unused MCP reload sentinel mechanism * fix: honor ignore_case for anchor_insert in text conversion path
2025-09-03 00:36:50 +08:00
// Execute synchronously. This code runs on the Editor main thread in our bridge path.
bool executed = EditorApplication.ExecuteMenuItem(menuPath);
if (executed)
{
// Success trace (debug-gated)
McpLog.Info($"[ExecuteMenuItem] Executed successfully: '{menuPath}'", always: false);
feat: Automatic Background Compile and Domain Reload for MCP Script Edits and New Script Creation (#248) * Unity MCP: reliable auto-reload via Unity-side sentinel flip; remove Python writes - Add MCP/Flip Reload Sentinel editor menu and flip package sentinel synchronously - Trigger sentinel flip after Create/Update/ApplyTextEdits (sync) in ManageScript - Instrument flip path with Debug.Log for traceability - Remove Python reload_sentinel writes; tools now execute Unity menu instead - Harden reload_sentinel path resolution to project/package - ExecuteMenuItem runs synchronously for deterministic results - Verified MCP edits trigger compile/reload without focus; no Python permission errors * Getting double flips * Fix double reload and ensure accurate batch edits; immediate structured reloads * Remove MCP/Flip Reload Sentinel menu; rely on synchronous import/compile for reloads * Route bridge/editor logs through McpLog and gate behind debug; create path now reloads synchronously * chore: ignore backup artifacts; remove stray ManageScript.cs.backup files * fix span logic * fix: honor UNITY_MCP_STATUS_DIR for sentinel status file lookup (fallback to ~/.unity-mcp) * test: add sentinel test honoring UNITY_MCP_STATUS_DIR; chore: ManageScript overlap check simplification and log consistency * Harden environment path, remove extraneous flip menu test * refactor: centralize import/compile via ManageScriptRefreshHelpers.ImportAndRequestCompile; replace duplicated sequences * feat: add scheduledRefresh flag; standardize logs; gate info and DRY immediate import/compile * chore: remove execute_menu_item sentinel flip from manage_script_edits; rely on import/compile * chore: remove unused MCP reload sentinel mechanism * fix: honor ignore_case for anchor_insert in text conversion path
2025-09-03 00:36:50 +08:00
return Response.Success(
$"Executed menu item: '{menuPath}'",
new { executed = true, menuPath }
);
}
Debug.LogWarning($"[ExecuteMenuItem] Failed (not found/disabled): '{menuPath}'");
return Response.Error(
$"Failed to execute menu item (not found or disabled): '{menuPath}'",
new { executed = false, menuPath }
);
2025-03-31 03:58:01 +08:00
}
catch (Exception e)
{
feat: Automatic Background Compile and Domain Reload for MCP Script Edits and New Script Creation (#248) * Unity MCP: reliable auto-reload via Unity-side sentinel flip; remove Python writes - Add MCP/Flip Reload Sentinel editor menu and flip package sentinel synchronously - Trigger sentinel flip after Create/Update/ApplyTextEdits (sync) in ManageScript - Instrument flip path with Debug.Log for traceability - Remove Python reload_sentinel writes; tools now execute Unity menu instead - Harden reload_sentinel path resolution to project/package - ExecuteMenuItem runs synchronously for deterministic results - Verified MCP edits trigger compile/reload without focus; no Python permission errors * Getting double flips * Fix double reload and ensure accurate batch edits; immediate structured reloads * Remove MCP/Flip Reload Sentinel menu; rely on synchronous import/compile for reloads * Route bridge/editor logs through McpLog and gate behind debug; create path now reloads synchronously * chore: ignore backup artifacts; remove stray ManageScript.cs.backup files * fix span logic * fix: honor UNITY_MCP_STATUS_DIR for sentinel status file lookup (fallback to ~/.unity-mcp) * test: add sentinel test honoring UNITY_MCP_STATUS_DIR; chore: ManageScript overlap check simplification and log consistency * Harden environment path, remove extraneous flip menu test * refactor: centralize import/compile via ManageScriptRefreshHelpers.ImportAndRequestCompile; replace duplicated sequences * feat: add scheduledRefresh flag; standardize logs; gate info and DRY immediate import/compile * chore: remove execute_menu_item sentinel flip from manage_script_edits; rely on import/compile * chore: remove unused MCP reload sentinel mechanism * fix: honor ignore_case for anchor_insert in text conversion path
2025-09-03 00:36:50 +08:00
Debug.LogError($"[ExecuteMenuItem] Error executing '{menuPath}': {e}");
return Response.Error($"Error executing menu item '{menuPath}': {e.Message}");
2025-03-31 03:58:01 +08:00
}
}
// TODO: Add helper for alias lookup if implementing aliases.
// private static string LookupAlias(string alias) { ... return actualMenuPath or null ... }
}
}