- Renamed all directories: OpenHarbor.MCP.* → Svrnty.MCP.* - Updated all namespaces in 179 C# files - Renamed 20 .csproj files and 3 .sln files - Updated 193 documentation references - Updated 33 references in main CODEX codebase - Updated Codex.sln with new paths - Build verified: 0 errors Preparing for extraction to standalone repositories. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
8.9 KiB
8.9 KiB
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:
public interface IMcpClient
{
// Connection Management
Task ConnectToServerAsync(string serverName, CancellationToken ct = default);
Task DisconnectFromServerAsync(string serverName, CancellationToken ct = default);
Task<IEnumerable<McpServerInfo>> GetConnectedServersAsync();
// Tool Discovery
Task<IEnumerable<McpToolInfo>> ListToolsAsync(string serverName, CancellationToken ct = default);
// Tool Execution
Task<McpToolResult> CallToolAsync(
string serverName,
string toolName,
Dictionary<string, object> arguments,
CancellationToken ct = default
);
// Health Monitoring
Task PingAsync(string serverName, CancellationToken ct = default);
}
IMcpServerConnection Interface
Represents connection to a single MCP server:
public interface IMcpServerConnection : IDisposable
{
string ServerName { get; }
bool IsConnected { get; }
DateTime LastActivity { get; }
Task<JsonRpcResponse> SendRequestAsync(
JsonRpcRequest request,
CancellationToken ct = default
);
Task ConnectAsync(CancellationToken ct = default);
Task DisconnectAsync(CancellationToken ct = default);
}
IConnectionPool Interface
Manages connection pooling:
public interface IConnectionPool
{
Task<IMcpServerConnection> 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:
public class StdioTransport : IMcpTransport
{
private readonly string _command;
private readonly string[] _args;
private Process? _process;
public async Task<JsonRpcResponse> SendAsync(
JsonRpcRequest request,
CancellationToken ct)
{
// Write JSON to stdin
// Read JSON from stdout
// Handle stderr logging
}
}
HTTP Transport
Communication via HTTP API:
public class HttpTransport : IMcpTransport
{
private readonly HttpClient _httpClient;
private readonly string _baseUrl;
public async Task<JsonRpcResponse> SendAsync(
JsonRpcRequest request,
CancellationToken ct)
{
// POST JSON to HTTP endpoint
// Deserialize response
}
}
Configuration
Server Configuration Model
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<string, string>? Headers { get; set; } // For HTTP
}
Connection Configuration
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
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<string, object> Arguments { get; }
}
public class McpTimeoutException : McpClientException { }
Retry Strategy
public class RetryPolicy
{
public async Task<T> ExecuteAsync<T>(
Func<Task<T>> 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