using System.Collections.Concurrent; namespace LazyBear.MCP.Services.ToolRegistry; /// /// Singleton. Хранит runtime-состояние включённости модулей и отдельных инструментов. /// Thread-safe. Уведомляет подписчиков при любом изменении. /// public sealed class ToolRegistryService { private readonly ConcurrentDictionary _moduleEnabled = new(StringComparer.OrdinalIgnoreCase); private readonly ConcurrentDictionary _toolEnabled = new(StringComparer.OrdinalIgnoreCase); private readonly List _modules = []; private readonly Lock _modulesLock = new(); /// Вызывается при любом изменении состояния. public event Action? StateChanged; // ── Регистрация ────────────────────────────────────────────────────────── public void RegisterModule(IToolModule module) { lock (_modulesLock) { if (_modules.Any(m => string.Equals(m.ModuleName, module.ModuleName, StringComparison.OrdinalIgnoreCase))) return; _modules.Add(module); } _moduleEnabled.TryAdd(module.ModuleName, true); foreach (var tool in module.ToolNames) { _toolEnabled.TryAdd(MakeKey(module.ModuleName, tool), true); } } // ── Запросы состояния ──────────────────────────────────────────────────── public IReadOnlyList GetModules() { lock (_modulesLock) { return _modules.ToList(); } } public bool IsModuleEnabled(string moduleName) => _moduleEnabled.GetValueOrDefault(moduleName, true); public bool IsToolConfiguredEnabled(string moduleName, string toolName) => _toolEnabled.GetValueOrDefault(MakeKey(moduleName, toolName), true); public bool IsToolEnabled(string moduleName, string toolName) => IsModuleEnabled(moduleName) && IsToolConfiguredEnabled(moduleName, toolName); // ── Переключение ───────────────────────────────────────────────────────── public void SetModuleEnabled(string moduleName, bool enabled) { _moduleEnabled[moduleName] = enabled; StateChanged?.Invoke(); } public void SetToolEnabled(string moduleName, string toolName, bool enabled) { _toolEnabled[MakeKey(moduleName, toolName)] = enabled; StateChanged?.Invoke(); } public void ToggleModule(string moduleName) => SetModuleEnabled(moduleName, !IsModuleEnabled(moduleName)); public void ToggleTool(string moduleName, string toolName) => SetToolEnabled(moduleName, toolName, !IsToolConfiguredEnabled(moduleName, toolName)); // ── Счётчики для Overview ───────────────────────────────────────────────── public (int Active, int Total) GetToolCounts(string moduleName) { lock (_modulesLock) { var module = _modules.FirstOrDefault(m => string.Equals(m.ModuleName, moduleName, StringComparison.OrdinalIgnoreCase)); if (module is null) return (0, 0); var total = module.ToolNames.Count; var active = module.ToolNames.Count(t => IsToolEnabled(moduleName, t)); return (active, total); } } public (int Enabled, int Total) GetConfiguredToolCounts(string moduleName) { lock (_modulesLock) { var module = _modules.FirstOrDefault(m => string.Equals(m.ModuleName, moduleName, StringComparison.OrdinalIgnoreCase)); if (module is null) return (0, 0); var total = module.ToolNames.Count; var enabled = module.ToolNames.Count(t => IsToolConfiguredEnabled(moduleName, t)); return (enabled, total); } } // ── Helpers ─────────────────────────────────────────────────────────────── private static string MakeKey(string module, string tool) => $"{module}::{tool}"; }