using OpenHarbor.MCP.Client.Core.Abstractions; using OpenHarbor.MCP.Client.Core.Exceptions; using OpenHarbor.MCP.Client.Core.Models; namespace OpenHarbor.MCP.Client.Infrastructure; /// /// Implements MCP client for managing multiple MCP server connections. /// Provides tool discovery and execution across configured servers. /// public class McpClient : IMcpClient { private readonly List _configs; private readonly Dictionary _connections; public McpClient(List configs) { _configs = configs ?? throw new ArgumentNullException(nameof(configs)); _connections = new Dictionary(); } public async Task ConnectAllAsync(CancellationToken cancellationToken = default) { foreach (var config in _configs.Where(c => c.Enabled)) { if (_connections.ContainsKey(config.Name)) { continue; } var connection = CreateConnection(config); await connection.ConnectAsync(cancellationToken); _connections[config.Name] = connection; } } public async Task DisconnectAllAsync(CancellationToken cancellationToken = default) { foreach (var connection in _connections.Values) { await connection.DisconnectAsync(cancellationToken); } _connections.Clear(); } public Task> GetConnectedServersAsync( CancellationToken cancellationToken = default) { var connectedConfigs = _configs .Where(c => _connections.ContainsKey(c.Name)) .ToList(); return Task.FromResult>(connectedConfigs); } public async Task> ListToolsAsync( string serverName, CancellationToken cancellationToken = default) { var connection = GetConnection(serverName); return await connection.ListToolsAsync(cancellationToken); } public async Task CallToolAsync( string serverName, string toolName, Dictionary? arguments = null, CancellationToken cancellationToken = default) { var connection = GetConnection(serverName); return await connection.CallToolAsync(toolName, arguments, cancellationToken); } public async Task PingAsync(string serverName, CancellationToken cancellationToken = default) { var connection = GetConnection(serverName); await connection.PingAsync(cancellationToken); } public async ValueTask DisposeAsync() { await DisconnectAllAsync(); foreach (var connection in _connections.Values) { await connection.DisposeAsync(); } _connections.Clear(); } private IMcpServerConnection GetConnection(string serverName) { if (!_connections.TryGetValue(serverName, out var connection)) { throw new ServerNotFoundException(serverName); } return connection; } private static IMcpServerConnection CreateConnection(McpServerConfig config) { return config.Transport switch { StdioTransportConfig => new StdioServerConnection(config), _ => throw new NotSupportedException( $"Transport type '{config.Transport.Type}' is not supported") }; } }