namespace AxCopilot.Services.Agent; /// /// 사용 가능한 에이전트 도구/스킬을 관리하는 레지스트리. /// 도구 목록을 LLM function calling에 전달하고, 이름으로 도구를 찾습니다. /// public class ToolRegistry : IDisposable { private readonly Dictionary _tools = new(StringComparer.OrdinalIgnoreCase); private readonly List _ownedResources = new(); private readonly Dictionary _mcpClients = new(StringComparer.OrdinalIgnoreCase); /// 등록된 모든 도구 목록. public IReadOnlyCollection All => _tools.Values; /// 도구를 이름으로 찾습니다. public IAgentTool? Get(string name) => _tools.TryGetValue(AgentToolCatalog.Canonicalize(name), out var tool) ? tool : null; /// 도구를 등록합니다. public void Register(IAgentTool tool) => _tools[tool.Name] = tool; public IReadOnlyCollection GetMcpClients() => _mcpClients.Values.ToList().AsReadOnly(); public async Task RegisterMcpToolsAsync(IEnumerable? servers, CancellationToken ct = default) { if (servers == null) return 0; var registered = 0; foreach (var server in servers) { if (server == null || !server.Enabled) continue; if (!string.Equals(server.Transport, "stdio", StringComparison.OrdinalIgnoreCase)) { LogService.Warn($"MCP '{server.Name}': unsupported transport '{server.Transport}'."); continue; } var client = new McpClientService(server); if (!await client.ConnectAsync(ct).ConfigureAwait(false)) { client.Dispose(); continue; } _ownedResources.Add(client); _mcpClients[server.Name] = client; foreach (var def in client.Tools) { Register(new McpTool(client, def)); registered++; } } McpSkillCatalog.RefreshFromClients(_mcpClients.Values); return registered; } /// 비활성 도구를 제외한 활성 도구 목록을 반환합니다. public IReadOnlyCollection GetActiveTools(IEnumerable? disabledNames = null) { if (disabledNames == null) return All; var disabled = new HashSet(AgentToolCatalog.CanonicalizeMany(disabledNames), StringComparer.OrdinalIgnoreCase); if (disabled.Count == 0) return All; return OrderToolsForExposure(_tools.Values.Where(t => !disabled.Contains(t.Name))) .ToList() .AsReadOnly(); } /// 비활성 도구를 제외하고 현재 탭에 해당하는 도구만 반환합니다. public IReadOnlyCollection GetActiveToolsForTab(string activeTab, IEnumerable? disabledNames = null) { var disabled = disabledNames != null ? new HashSet(AgentToolCatalog.CanonicalizeMany(disabledNames), StringComparer.OrdinalIgnoreCase) : null; return OrderToolsForExposure(_tools.Values.Where(t => { if (disabled != null && disabled.Contains(t.Name)) return false; return IsToolAvailableForTab(t, activeTab); })).ToList().AsReadOnly(); } private static IEnumerable OrderToolsForExposure(IEnumerable tools) { return tools .OrderBy(GetToolExposureBucket) .ThenBy(t => t.Name, StringComparer.OrdinalIgnoreCase); } private static int GetToolExposureBucket(IAgentTool tool) { return AgentToolCatalog.GetExposureBucket(tool.Name); } /// 도구가 해당 탭에서 사용 가능한지 확인합니다. private static bool IsToolAvailableForTab(IAgentTool tool, string activeTab) { var category = ResolveTabCategory(tool); if (string.IsNullOrEmpty(category)) return true; // null = 모든 탭 // 쉼표 구분 복수 탭: "Cowork,Code" foreach (var part in category.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)) { if (string.Equals(part, activeTab, StringComparison.OrdinalIgnoreCase)) return true; } return false; } /// /// 도구별 탭 카테고리 오버라이드. /// IAgentTool.TabCategory가 null인 도구는 이 맵을 참조합니다. /// 키: 도구 이름, 값: 허용 탭 (쉼표 구분). 맵에 없으면 = 모든 탭. /// /// 도구의 실질 탭 카테고리를 결정합니다 (IAgentTool.TabCategory → 오버라이드 맵 순). private static string? ResolveTabCategory(IAgentTool tool) { // 도구 자체에 TabCategory가 명시되어 있으면 우선 if (!string.IsNullOrEmpty(tool.TabCategory)) return tool.TabCategory; return AgentToolCatalog.GetTabCategory(tool.Name); } /// IDisposable 도구를 모두 해제합니다. public void Dispose() { foreach (var tool in _tools.Values) { if (tool is IDisposable disposable) disposable.Dispose(); } foreach (var resource in _ownedResources) resource.Dispose(); _ownedResources.Clear(); _mcpClients.Clear(); _tools.Clear(); } /// 기본 도구 + 내장 스킬을 모두 등록한 레지스트리를 생성합니다. public static ToolRegistry CreateDefault() { var registry = new ToolRegistry(); // 기본 도구 (파일/검색/프로세스) registry.Register(new FileReadTool()); registry.Register(new FileWriteTool()); registry.Register(new FileEditTool()); registry.Register(new GlobTool()); registry.Register(new GrepTool()); registry.Register(new ProcessTool()); registry.Register(new FolderMapTool()); registry.Register(new DocumentReaderTool()); // 내장 스킬 (문서 생성) registry.Register(new ExcelSkill()); registry.Register(new DocxSkill()); registry.Register(new CsvSkill()); registry.Register(new MarkdownSkill()); registry.Register(new HtmlSkill()); registry.Register(new ChartSkill()); registry.Register(new BatchSkill()); registry.Register(new PptxSkill()); // 멀티패스 문서 엔진 registry.Register(new DocumentPlannerTool()); registry.Register(new DocumentAssemblerTool()); // 문서 품질 검증 & 포맷 변환 registry.Register(new DocumentReviewTool()); registry.Register(new FormatConvertTool()); // Code 탭: 개발 환경 감지 & 빌드/테스트 & Git registry.Register(new DevEnvDetectTool()); registry.Register(new BuildRunTool()); registry.Register(new GitTool()); registry.Register(new LspTool()); registry.Register(new SubAgentTool()); registry.Register(new SpawnAgentsTool()); registry.Register(new WaitAgentsTool()); registry.Register(new CodeSearchTool()); registry.Register(new TestLoopTool()); registry.Register(new ToolSearchTool(() => registry.All)); // 코드 리뷰 + 프로젝트 규칙 registry.Register(new CodeReviewTool()); registry.Register(new ProjectRuleTool()); // 스킬 시스템 registry.Register(new SkillManagerTool()); // 에이전트 메모리 registry.Register(new MemoryTool()); // 데이터 처리 + 시스템 유틸리티 registry.Register(new JsonTool()); registry.Register(new RegexTool()); registry.Register(new DiffTool()); registry.Register(new ClipboardTool()); registry.Register(new NotifyTool()); registry.Register(new EnvTool()); registry.Register(new ZipTool()); registry.Register(new HttpTool()); registry.Register(new SqlTool()); registry.Register(new Base64Tool()); registry.Register(new HashTool()); registry.Register(new DateTimeTool()); // 코드 품질 registry.Register(new SnippetRunnerTool()); // 데이터 분석 + 문서 자동화 registry.Register(new DataPivotTool()); registry.Register(new TemplateRenderTool()); registry.Register(new TextSummarizeTool()); // 파일 모니터링 + 이미지 분석 registry.Register(new FileWatchTool()); registry.Register(new ImageAnalyzeTool()); // 파일 관리 + 메타데이터 + 멀티리드 registry.Register(new FileManageTool()); registry.Register(new FileInfoTool()); registry.Register(new MultiReadTool()); // 사용자 질문 registry.Register(new UserAskTool()); // MCP 리소스 registry.Register(new McpListResourcesTool(() => registry.GetMcpClients())); registry.Register(new McpReadResourceTool(() => registry.GetMcpClients())); // 외부 열기 + 수학 + XML + 인코딩 registry.Register(new OpenExternalTool()); registry.Register(new MathTool()); registry.Register(new XmlTool()); registry.Register(new EncodingTool()); // 태스크 추적 registry.Register(new TaskTrackerTool()); registry.Register(new TodoWriteTool()); registry.Register(new TaskCreateTool()); registry.Register(new TaskGetTool()); registry.Register(new TaskListTool()); registry.Register(new TaskUpdateTool()); registry.Register(new TaskStopTool()); registry.Register(new TaskOutputTool()); registry.Register(new EnterWorktreeTool()); registry.Register(new ExitWorktreeTool()); registry.Register(new TeamCreateTool()); registry.Register(new TeamDeleteTool()); registry.Register(new CronCreateTool()); registry.Register(new CronDeleteTool()); registry.Register(new CronListTool()); // 워크플로우 도구 registry.Register(new SuggestActionsTool()); registry.Register(new DiffPreviewTool()); registry.Register(new CheckpointTool()); registry.Register(new PlaybookTool()); return registry; } }