using ColaFlow.Modules.Mcp.Contracts.Tools; using ColaFlow.Modules.Mcp.Domain.Exceptions; using Microsoft.Extensions.Logging; namespace ColaFlow.Modules.Mcp.Application.Services; /// /// Registry for MCP Tools with auto-discovery /// Uses constructor injection to register all IMcpTool implementations /// public class McpToolRegistry : IMcpToolRegistry { private readonly Dictionary _tools; private readonly ILogger _logger; public McpToolRegistry( IEnumerable tools, ILogger _logger) { this._logger = _logger ?? throw new ArgumentNullException(nameof(_logger)); // Auto-discover and register all tools _tools = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (var tool in tools) { if (_tools.ContainsKey(tool.Name)) { this._logger.LogWarning( "Duplicate tool name detected: {ToolName}. Skipping duplicate registration.", tool.Name); continue; } _tools[tool.Name] = tool; this._logger.LogInformation( "Registered MCP Tool: {ToolName} - {Description}", tool.Name, tool.Description); } this._logger.LogInformation( "McpToolRegistry initialized with {Count} tools", _tools.Count); } public IEnumerable GetAllTools() { return _tools.Values; } public IMcpTool? GetTool(string toolName) { if (string.IsNullOrWhiteSpace(toolName)) return null; _tools.TryGetValue(toolName, out var tool); return tool; } public bool HasTool(string toolName) { if (string.IsNullOrWhiteSpace(toolName)) return false; return _tools.ContainsKey(toolName); } public async Task ExecuteToolAsync( McpToolCall toolCall, CancellationToken cancellationToken = default) { if (toolCall == null) throw new ArgumentNullException(nameof(toolCall)); if (string.IsNullOrWhiteSpace(toolCall.Name)) throw new McpInvalidParamsException("Tool name cannot be empty"); // Get tool var tool = GetTool(toolCall.Name); if (tool == null) { _logger.LogWarning( "Tool not found: {ToolName}. Available tools: {AvailableTools}", toolCall.Name, string.Join(", ", _tools.Keys)); throw new McpNotFoundException("Tool", toolCall.Name); } _logger.LogInformation( "Executing MCP Tool: {ToolName}", toolCall.Name); try { // Execute tool var result = await tool.ExecuteAsync(toolCall, cancellationToken); _logger.LogInformation( "MCP Tool executed successfully: {ToolName}, IsError={IsError}", toolCall.Name, result.IsError); return result; } catch (Exception ex) { _logger.LogError(ex, "Error executing MCP Tool: {ToolName}", toolCall.Name); // Return error result return new McpToolResult { Content = new[] { new McpToolContent { Type = "text", Text = $"Error executing tool '{toolCall.Name}': {ex.Message}" } }, IsError = true }; } } }