svrnty-mcp-client/docs/module-design.md
Svrnty d936ad7856 docs: comprehensive AI coding assistant research and MCP-first implementation plan
Research conducted on modern AI coding assistants (Cursor, GitHub Copilot, Cline,
Aider, Windsurf, Replit Agent) to understand architecture patterns, context management,
code editing workflows, and tool use protocols.

Key Decision: Pivoted from building full CLI (40-50h) to validation-driven MCP-first
approach (10-15h). Build 5 core CODEX MCP tools that work with ANY coding assistant,
validate adoption over 2-4 weeks, then decide on full CLI if demand proven.

Files:
- research/ai-systems/modern-coding-assistants-architecture.md (comprehensive research)
- research/ai-systems/codex-coding-assistant-implementation-plan.md (original CLI plan, preserved)
- research/ai-systems/codex-mcp-tools-implementation-plan.md (approved MCP-first plan)
- ideas/registry.json (updated with approved MCP tools proposal)

Architech Validation: APPROVED with pivot to MCP-first approach
Human Decision: Approved (pragmatic validation-driven development)

Next: Begin Phase 1 implementation (10-15 hours, 5 core MCP tools)

🤖 Generated with CODEX Research System

Co-Authored-By: The Archivist <archivist@codex.svrnty.io>
Co-Authored-By: The Architech <architech@codex.svrnty.io>
Co-Authored-By: Mathias Beaulieu-Duncan <mat@svrnty.io>
2025-10-22 21:00:34 -04:00

8.9 KiB

OpenHarbor.MCP.Client - Module Design

Document Type: Architecture Design Document Status: Planned Version: 1.0.0 Last Updated: 2025-10-19


Overview

OpenHarbor.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

┌─────────────────────────────────────────────────┐
│       OpenHarbor.MCP.Client.Cli (Executable)    │
│  ┌───────────────────────────────────────────┐  │
│  │  OpenHarbor.MCP.Client.AspNetCore (DI)   │  │
│  │  ┌─────────────────────────────────────┐ │  │
│  │  │ OpenHarbor.MCP.Client.Infrastructure│ │  │
│  │  │  ┌───────────────────────────────┐  │ │  │
│  │  │  │ OpenHarbor.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