- 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>
341 lines
8.9 KiB
Markdown
341 lines
8.9 KiB
Markdown
# 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<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:
|
|
|
|
```csharp
|
|
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:
|
|
|
|
```csharp
|
|
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:
|
|
|
|
```csharp
|
|
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:
|
|
|
|
```csharp
|
|
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
|
|
|
|
```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<string, string>? 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<string, object> Arguments { get; }
|
|
}
|
|
|
|
public class McpTimeoutException : McpClientException { }
|
|
```
|
|
|
|
### Retry Strategy
|
|
|
|
```csharp
|
|
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
|
|
|