# Svrnty.MCP.Client - Module Design **Document Type:** Architecture Design Document **Status:** Planned **Version:** 1.0.0 **Last Updated:** 2025-10-19 --- ## Overview Svrnty.MCP.Client is a .NET 8 library that enables applications to act as MCP clients, consuming tools exposed by remote MCP servers. This document defines the architecture, components, and design decisions. ### Purpose - **What**: Client library for connecting to and calling tools from MCP servers - **Why**: Enable .NET applications to consume MCP server capabilities - **How**: Clean Architecture with transport abstractions, connection pooling, and error handling --- ## Architecture ### Clean Architecture Layers ``` ┌─────────────────────────────────────────────────┐ │ Svrnty.MCP.Client.Cli (Executable) │ │ ┌───────────────────────────────────────────┐ │ │ │ Svrnty.MCP.Client.AspNetCore (DI) │ │ │ │ ┌─────────────────────────────────────┐ │ │ │ │ │ Svrnty.MCP.Client.Infrastructure│ │ │ │ │ │ ┌───────────────────────────────┐ │ │ │ │ │ │ │ Svrnty.MCP.Client.Core │ │ │ │ │ │ │ │ - IMcpClient │ │ │ │ │ │ │ │ - IMcpServerConnection │ │ │ │ │ │ │ │ - IConnectionPool │ │ │ │ │ │ │ │ - Models (no dependencies) │ │ │ │ │ │ │ └───────────────────────────────┘ │ │ │ │ │ └─────────────────────────────────────┘ │ │ │ └───────────────────────────────────────────┘ │ └─────────────────────────────────────────────────┘ ``` ### Layer Responsibilities | Layer | Purpose | Dependencies | |-------|---------|--------------| | **Core** | Abstractions and models | None | | **Infrastructure** | Transport implementations | Core, System.Text.Json | | **AspNetCore** | DI and configuration | Core, Infrastructure, ASP.NET Core | | **Cli** | Command-line interface | All layers | --- ## Core Components ### IMcpClient Interface Primary interface for client operations: ```csharp public interface IMcpClient { // Connection Management Task ConnectToServerAsync(string serverName, CancellationToken ct = default); Task DisconnectFromServerAsync(string serverName, CancellationToken ct = default); Task> GetConnectedServersAsync(); // Tool Discovery Task> ListToolsAsync(string serverName, CancellationToken ct = default); // Tool Execution Task CallToolAsync( string serverName, string toolName, Dictionary arguments, CancellationToken ct = default ); // Health Monitoring Task PingAsync(string serverName, CancellationToken ct = default); } ``` ### IMcpServerConnection Interface Represents connection to a single MCP server: ```csharp public interface IMcpServerConnection : IDisposable { string ServerName { get; } bool IsConnected { get; } DateTime LastActivity { get; } Task SendRequestAsync( JsonRpcRequest request, CancellationToken ct = default ); Task ConnectAsync(CancellationToken ct = default); Task DisconnectAsync(CancellationToken ct = default); } ``` ### IConnectionPool Interface Manages connection pooling: ```csharp public interface IConnectionPool { Task AcquireConnectionAsync( string serverName, CancellationToken ct = default ); void ReleaseConnection(IMcpServerConnection connection); Task EvictIdleConnectionsAsync(CancellationToken ct = default); } ``` --- ## Transport Layer ### Supported Transports #### Stdio Transport Communication via standard input/output with spawned process: ```csharp public class StdioTransport : IMcpTransport { private readonly string _command; private readonly string[] _args; private Process? _process; public async Task SendAsync( JsonRpcRequest request, CancellationToken ct) { // Write JSON to stdin // Read JSON from stdout // Handle stderr logging } } ``` #### HTTP Transport Communication via HTTP API: ```csharp public class HttpTransport : IMcpTransport { private readonly HttpClient _httpClient; private readonly string _baseUrl; public async Task SendAsync( JsonRpcRequest request, CancellationToken ct) { // POST JSON to HTTP endpoint // Deserialize response } } ``` --- ## Configuration ### Server Configuration Model ```csharp public class McpServerConfig { public string Name { get; set; } public string? Description { get; set; } public TransportConfig Transport { get; set; } public TimeSpan Timeout { get; set; } = TimeSpan.FromSeconds(30); public bool Enabled { get; set; } = true; } public class TransportConfig { public string Type { get; set; } // "Stdio" or "Http" public string? Command { get; set; } // For Stdio public string[]? Args { get; set; } // For Stdio public string? BaseUrl { get; set; } // For HTTP public Dictionary? Headers { get; set; } // For HTTP } ``` ### Connection Configuration ```csharp public class ConnectionConfig { public int MaxRetries { get; set; } = 3; public int RetryDelayMs { get; set; } = 1000; public double RetryBackoffMultiplier { get; set; } = 2.0; public bool EnableConnectionPooling { get; set; } = true; public int MaxConnectionsPerServer { get; set; } = 5; public TimeSpan ConnectionIdleTimeout { get; set; } = TimeSpan.FromMinutes(5); public TimeSpan PoolEvictionInterval { get; set; } = TimeSpan.FromMinutes(1); } ``` --- ## Error Handling ### Exception Hierarchy ```csharp public class McpClientException : Exception { } public class McpConnectionException : McpClientException { } public class McpToolNotFoundException : McpClientException { } public class McpToolExecutionException : McpClientException { public string ServerName { get; } public string ToolName { get; } public Dictionary Arguments { get; } } public class McpTimeoutException : McpClientException { } ``` ### Retry Strategy ```csharp public class RetryPolicy { public async Task ExecuteAsync( Func> operation, int maxRetries, int delayMs, double backoffMultiplier) { for (int attempt = 0; attempt < maxRetries; attempt++) { try { return await operation(); } catch (McpConnectionException) when (attempt < maxRetries - 1) { var delay = delayMs * Math.Pow(backoffMultiplier, attempt); await Task.Delay((int)delay); } } throw new McpConnectionException("Max retries exceeded"); } } ``` --- ## Testing Strategy ### Unit Tests - Test Core abstractions with mocks - Test Infrastructure implementations with mock transports - Test retry logic and connection pooling ### Integration Tests - Test actual connections to mock MCP servers - Test tool discovery and execution - Test error scenarios (timeouts, connection failures) ### Test Coverage Goals - Core: >90% - Infrastructure: >80% - AspNetCore: >70% --- ## Performance Considerations ### Connection Pooling - Reuse connections to avoid process spawn overhead - Configurable pool size per server - Idle connection eviction ### Request Pipelining - Support concurrent requests to same server - Queue requests when connection limit reached ### Timeout Management - Per-server configurable timeouts - Cancellation token support throughout --- ## Security ### Input Validation - Validate server configuration - Sanitize arguments before sending - Validate JSON responses ### Connection Security - HTTPS for HTTP transport - Environment variable expansion for secrets - No hardcoded credentials --- ## Future Enhancements - [ ] WebSocket transport support - [ ] Request/response compression - [ ] Metrics and observability (OpenTelemetry) - [ ] Connection health monitoring - [ ] Circuit breaker pattern - [ ] Server discovery mechanism --- **Document Version:** 1.0.0 **Status:** Planned **Next Review:** After Phase 1 implementation