using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using MCPForUnity.Editor.Constants; using MCPForUnity.Editor.Helpers; using MCPForUnity.Editor.Resources; using UnityEditor; namespace MCPForUnity.Editor.Services { public class ResourceDiscoveryService : IResourceDiscoveryService { private Dictionary _cachedResources; public List DiscoverAllResources() { if (_cachedResources != null) { return _cachedResources.Values.ToList(); } _cachedResources = new Dictionary(); var resourceTypes = TypeCache.GetTypesWithAttribute(); foreach (var type in resourceTypes) { McpForUnityResourceAttribute resourceAttr; try { resourceAttr = type.GetCustomAttribute(); } catch (Exception ex) { McpLog.Warn($"Failed to read [McpForUnityResource] for {type.FullName}: {ex.Message}"); continue; } if (resourceAttr == null) { continue; } var metadata = ExtractResourceMetadata(type, resourceAttr); if (metadata != null) { if (_cachedResources.ContainsKey(metadata.Name)) { McpLog.Warn($"Duplicate resource name '{metadata.Name}' from {type.FullName}; overwriting previous registration."); } _cachedResources[metadata.Name] = metadata; EnsurePreferenceInitialized(metadata); } } McpLog.Info($"Discovered {_cachedResources.Count} MCP resources via reflection", false); return _cachedResources.Values.ToList(); } public ResourceMetadata GetResourceMetadata(string resourceName) { if (string.IsNullOrEmpty(resourceName)) { return null; } if (_cachedResources == null) { DiscoverAllResources(); } return _cachedResources.TryGetValue(resourceName, out var metadata) ? metadata : null; } public List GetEnabledResources() { return DiscoverAllResources() .Where(r => IsResourceEnabled(r.Name)) .ToList(); } public bool IsResourceEnabled(string resourceName) { if (string.IsNullOrEmpty(resourceName)) { return false; } string key = GetResourcePreferenceKey(resourceName); if (EditorPrefs.HasKey(key)) { return EditorPrefs.GetBool(key, true); } // Default: all resources enabled return true; } public void SetResourceEnabled(string resourceName, bool enabled) { if (string.IsNullOrEmpty(resourceName)) { return; } string key = GetResourcePreferenceKey(resourceName); EditorPrefs.SetBool(key, enabled); } public void InvalidateCache() { _cachedResources = null; } private ResourceMetadata ExtractResourceMetadata(Type type, McpForUnityResourceAttribute resourceAttr) { try { string resourceName = resourceAttr.ResourceName; if (string.IsNullOrEmpty(resourceName)) { resourceName = StringCaseUtility.ToSnakeCase(type.Name); } string description = resourceAttr.Description ?? $"Resource: {resourceName}"; var metadata = new ResourceMetadata { Name = resourceName, Description = description, ClassName = type.Name, Namespace = type.Namespace ?? "", AssemblyName = type.Assembly.GetName().Name }; metadata.IsBuiltIn = StringCaseUtility.IsBuiltInMcpType( type, metadata.AssemblyName, "MCPForUnity.Editor.Resources"); return metadata; } catch (Exception ex) { McpLog.Error($"Failed to extract metadata for resource {type.Name}: {ex.Message}"); return null; } } private void EnsurePreferenceInitialized(ResourceMetadata metadata) { if (metadata == null || string.IsNullOrEmpty(metadata.Name)) { return; } string key = GetResourcePreferenceKey(metadata.Name); if (!EditorPrefs.HasKey(key)) { EditorPrefs.SetBool(key, true); } } private static string GetResourcePreferenceKey(string resourceName) { return EditorPrefKeys.ResourceEnabledPrefix + resourceName; } } }