diff --git a/MCPForUnity/Editor/Services/IToolDiscoveryService.cs b/MCPForUnity/Editor/Services/IToolDiscoveryService.cs index 300f00e..ee2c616 100644 --- a/MCPForUnity/Editor/Services/IToolDiscoveryService.cs +++ b/MCPForUnity/Editor/Services/IToolDiscoveryService.cs @@ -14,7 +14,6 @@ namespace MCPForUnity.Editor.Services public string ClassName { get; set; } public string Namespace { get; set; } public string AssemblyName { get; set; } - public string AssetPath { get; set; } public bool AutoRegister { get; set; } = true; public bool RequiresPolling { get; set; } = false; public string PollAction { get; set; } = "status"; diff --git a/MCPForUnity/Editor/Services/ToolDiscoveryService.cs b/MCPForUnity/Editor/Services/ToolDiscoveryService.cs index 0dcfc79..329511f 100644 --- a/MCPForUnity/Editor/Services/ToolDiscoveryService.cs +++ b/MCPForUnity/Editor/Services/ToolDiscoveryService.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; using System.Linq; using System.Reflection; -using System.Text.RegularExpressions; using MCPForUnity.Editor.Constants; using MCPForUnity.Editor.Helpers; using MCPForUnity.Editor.Tools; @@ -13,8 +12,7 @@ namespace MCPForUnity.Editor.Services public class ToolDiscoveryService : IToolDiscoveryService { private Dictionary _cachedTools; - private readonly Dictionary _scriptPathCache = new(); - private readonly Dictionary _summaryCache = new(); + public List DiscoverAllTools() { @@ -25,33 +23,30 @@ namespace MCPForUnity.Editor.Services _cachedTools = new Dictionary(); - // Scan all assemblies for [McpForUnityTool] attributes - var assemblies = AppDomain.CurrentDomain.GetAssemblies(); - - foreach (var assembly in assemblies) + var toolTypes = TypeCache.GetTypesWithAttribute(); + foreach (var type in toolTypes) { + McpForUnityToolAttribute toolAttr; try { - var types = assembly.GetTypes(); - - foreach (var type in types) - { - var toolAttr = type.GetCustomAttribute(); - if (toolAttr == null) - continue; - - var metadata = ExtractToolMetadata(type, toolAttr); - if (metadata != null) - { - _cachedTools[metadata.Name] = metadata; - EnsurePreferenceInitialized(metadata); - } - } + toolAttr = type.GetCustomAttribute(); } catch (Exception ex) { - // Skip assemblies that can't be reflected - McpLog.Info($"Skipping assembly {assembly.FullName}: {ex.Message}"); + McpLog.Warn($"Failed to read [McpForUnityTool] for {type.FullName}: {ex.Message}"); + continue; + } + + if (toolAttr == null) + { + continue; + } + + var metadata = ExtractToolMetadata(type, toolAttr); + if (metadata != null) + { + _cachedTools[metadata.Name] = metadata; + EnsurePreferenceInitialized(metadata); } } @@ -131,23 +126,15 @@ namespace MCPForUnity.Editor.Services ClassName = type.Name, Namespace = type.Namespace ?? "", AssemblyName = type.Assembly.GetName().Name, - AssetPath = ResolveScriptAssetPath(type), AutoRegister = toolAttr.AutoRegister, RequiresPolling = toolAttr.RequiresPolling, PollAction = string.IsNullOrEmpty(toolAttr.PollAction) ? "status" : toolAttr.PollAction }; metadata.IsBuiltIn = DetermineIsBuiltIn(type, metadata); - if (metadata.IsBuiltIn) - { - string summaryDescription = ExtractSummaryDescription(type, metadata); - if (!string.IsNullOrWhiteSpace(summaryDescription)) - { - metadata.Description = summaryDescription; - } - } + return metadata; - + } catch (Exception ex) { @@ -265,56 +252,6 @@ namespace MCPForUnity.Editor.Services return EditorPrefKeys.ToolEnabledPrefix + toolName; } - private string ResolveScriptAssetPath(Type type) - { - if (type == null) - { - return null; - } - - if (_scriptPathCache.TryGetValue(type, out var cachedPath)) - { - return cachedPath; - } - - string resolvedPath = null; - - try - { - string filter = string.IsNullOrEmpty(type.Name) ? "t:MonoScript" : $"{type.Name} t:MonoScript"; - var guids = AssetDatabase.FindAssets(filter); - - foreach (var guid in guids) - { - string assetPath = AssetDatabase.GUIDToAssetPath(guid); - if (string.IsNullOrEmpty(assetPath)) - { - continue; - } - - var script = AssetDatabase.LoadAssetAtPath(assetPath); - if (script == null) - { - continue; - } - - var scriptClass = script.GetClass(); - if (scriptClass == type) - { - resolvedPath = assetPath.Replace('\\', '/'); - break; - } - } - } - catch (Exception ex) - { - McpLog.Warn($"Failed to resolve asset path for {type.FullName}: {ex.Message}"); - } - - _scriptPathCache[type] = resolvedPath; - return resolvedPath; - } - private bool DetermineIsBuiltIn(Type type, ToolMetadata metadata) { if (metadata == null) @@ -322,25 +259,9 @@ namespace MCPForUnity.Editor.Services return false; } - if (!string.IsNullOrEmpty(metadata.AssetPath)) + if (type != null && !string.IsNullOrEmpty(type.Namespace) && type.Namespace.StartsWith("MCPForUnity.Editor.Tools", StringComparison.Ordinal)) { - string normalizedPath = metadata.AssetPath.Replace("\\", "/"); - string packageRoot = AssetPathUtility.GetMcpPackageRootPath(); - - if (!string.IsNullOrEmpty(packageRoot)) - { - string normalizedRoot = packageRoot.Replace("\\", "/"); - if (!normalizedRoot.EndsWith("/", StringComparison.Ordinal)) - { - normalizedRoot += "/"; - } - - string builtInRoot = normalizedRoot + "Editor/Tools/"; - if (normalizedPath.StartsWith(builtInRoot, StringComparison.OrdinalIgnoreCase)) - { - return true; - } - } + return true; } if (!string.IsNullOrEmpty(metadata.AssemblyName) && metadata.AssemblyName.Equals("MCPForUnity.Editor", StringComparison.Ordinal)) @@ -350,57 +271,5 @@ namespace MCPForUnity.Editor.Services return false; } - - private string ExtractSummaryDescription(Type type, ToolMetadata metadata) - { - if (metadata == null || string.IsNullOrEmpty(metadata.AssetPath)) - { - return null; - } - - if (_summaryCache.TryGetValue(metadata.AssetPath, out var cachedSummary)) - { - return cachedSummary; - } - - string summary = null; - - try - { - var monoScript = AssetDatabase.LoadAssetAtPath(metadata.AssetPath); - string scriptText = monoScript?.text; - if (string.IsNullOrEmpty(scriptText)) - { - _summaryCache[metadata.AssetPath] = null; - return null; - } - - string classPattern = $@"///\s*\s*(?[\s\S]*?)\s*\s*(?:\[[^\]]*\]\s*)*(?:public\s+)?(?:static\s+)?class\s+{Regex.Escape(type.Name)}"; - var match = Regex.Match(scriptText, classPattern); - - if (!match.Success) - { - match = Regex.Match(scriptText, @"///\s*\s*(?[\s\S]*?)\s*"); - } - - if (!match.Success) - { - _summaryCache[metadata.AssetPath] = null; - return null; - } - - summary = match.Groups["content"].Value; - summary = Regex.Replace(summary, @"^\s*///\s?", string.Empty, RegexOptions.Multiline); - summary = Regex.Replace(summary, @"<[^>]+>", string.Empty); - summary = Regex.Replace(summary, @"\s+", " ").Trim(); - } - catch (System.Exception ex) - { - McpLog.Warn($"Failed to extract summary description for {type?.FullName}: {ex.Message}"); - } - - _summaryCache[metadata.AssetPath] = summary; - return summary; - } } } diff --git a/MCPForUnity/Editor/Windows/MCPForUnityEditorWindow.cs b/MCPForUnity/Editor/Windows/MCPForUnityEditorWindow.cs index d1932b1..b82f03c 100644 --- a/MCPForUnity/Editor/Windows/MCPForUnityEditorWindow.cs +++ b/MCPForUnity/Editor/Windows/MCPForUnityEditorWindow.cs @@ -2,17 +2,17 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using MCPForUnity.Editor.Constants; using MCPForUnity.Editor.Helpers; using MCPForUnity.Editor.Services; using MCPForUnity.Editor.Windows.Components.ClientConfig; using MCPForUnity.Editor.Windows.Components.Connection; using MCPForUnity.Editor.Windows.Components.Settings; +using MCPForUnity.Editor.Windows.Components.Tools; using UnityEditor; using UnityEditor.UIElements; using UnityEngine; using UnityEngine.UIElements; -using MCPForUnity.Editor.Constants; -using MCPForUnity.Editor.Windows.Components.Tools; namespace MCPForUnity.Editor.Windows { @@ -31,6 +31,7 @@ namespace MCPForUnity.Editor.Windows private static readonly HashSet OpenWindows = new(); private bool guiCreated = false; + private bool toolsLoaded = false; private double lastRefreshTime = 0; private const double RefreshDebounceSeconds = 0.5; @@ -196,18 +197,39 @@ namespace MCPForUnity.Editor.Windows var toolsRoot = toolsTree.Instantiate(); toolsContainer.Add(toolsRoot); toolsSection = new McpToolsSection(toolsRoot); - toolsSection.Refresh(); + + if (toolsTabToggle != null && toolsTabToggle.value) + { + EnsureToolsLoaded(); + } } else { McpLog.Warn("Failed to load tools section UXML. Tool configuration will be unavailable."); } + guiCreated = true; // Initial updates RefreshAllData(); } + private void EnsureToolsLoaded() + { + if (toolsLoaded) + { + return; + } + + if (toolsSection == null) + { + return; + } + + toolsLoaded = true; + toolsSection.Refresh(); + } + private void OnEnable() { EditorApplication.update += OnEditorUpdate; @@ -219,6 +241,7 @@ namespace MCPForUnity.Editor.Windows EditorApplication.update -= OnEditorUpdate; OpenWindows.Remove(this); guiCreated = false; + toolsLoaded = false; } private void OnFocus() @@ -327,6 +350,11 @@ namespace MCPForUnity.Editor.Windows settingsTabToggle?.SetValueWithoutNotify(showSettings); toolsTabToggle?.SetValueWithoutNotify(!showSettings); + if (!showSettings) + { + EnsureToolsLoaded(); + } + EditorPrefs.SetString(EditorPrefKeys.EditorWindowActivePanel, panel.ToString()); } diff --git a/TestProjects/UnityMCPTests/Assets/Temp.meta b/TestProjects/UnityMCPTests/Assets/Temp.meta deleted file mode 100644 index afa7b05..0000000 --- a/TestProjects/UnityMCPTests/Assets/Temp.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 20332651bb6f64cadb92cf3c6d68bed5 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: