svrnty-mcp-client/docs/deployment/https-setup.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

17 KiB

HTTPS/TLS Setup Guide - OpenHarbor.MCP.Client

Purpose: Secure HTTPS/TLS configuration for MCP client connections Audience: Application developers, system integrators Last Updated: 2025-10-19 Version: 1.0.0


Table of Contents

  1. Overview
  2. Prerequisites
  3. Client Configuration
  4. Certificate Validation
  5. Authentication
  6. Error Handling
  7. Best Practices
  8. Troubleshooting

Overview

OpenHarbor.MCP.Client supports HTTPS connections to MCP servers with full TLS certificate validation, custom certificate handling, and API key authentication.

Security Features:

  • Encrypted communication (TLS 1.2+)
  • Server certificate validation
  • Custom certificate authority support
  • Client certificate authentication (mutual TLS)
  • API key authentication via headers

Default Behavior:

  • HTTPS enabled by default when URL starts with https://
  • Certificate validation enabled (can be customized)
  • Connection pooling for performance
  • Timeout configuration (default: 30 seconds)

Prerequisites

Development

  • .NET 8.0 SDK
  • Access to MCP server with HTTPS enabled
  • API key for authentication (if required)

Production

  • Valid server certificates
  • Network connectivity to server
  • Firewall allows outbound HTTPS (port 443)

Client Configuration

Basic HTTPS Connection

Example 1: Simple HTTPS Client

using OpenHarbor.MCP.Client;

// Connect to HTTPS server
var config = new HttpServerConnectionConfig
{
    ServerUrl = "https://mcp.example.com",  // HTTPS URL
    ApiKey = "your-api-key-here"            // Authentication
};

using var connection = new HttpServerConnection(config);

// Use connection
var tools = await connection.ListToolsAsync();

Configuration Options:

public class HttpServerConnectionConfig
{
    /// <summary>
    /// Server URL (must start with https:// for secure connection)
    /// </summary>
    public string ServerUrl { get; set; } = "https://localhost:5051";

    /// <summary>
    /// API key for authentication (sent via X-API-Key header)
    /// </summary>
    public string? ApiKey { get; set; }

    /// <summary>
    /// Request timeout (default: 30 seconds)
    /// </summary>
    public TimeSpan Timeout { get; set; } = TimeSpan.FromSeconds(30);

    /// <summary>
    /// Maximum connections per server (default: 10)
    /// </summary>
    public int MaxConnectionsPerServer { get; set; } = 10;

    /// <summary>
    /// Custom certificate validation callback
    /// </summary>
    public Func<HttpRequestMessage, X509Certificate2?, X509Chain?, SslPolicyErrors, bool>? ServerCertificateCustomValidation { get; set; }

    /// <summary>
    /// Client certificate for mutual TLS (optional)
    /// </summary>
    public X509Certificate2? ClientCertificate { get; set; }
}

Example 2: Development with Self-Signed Certificate

For development servers using self-signed certificates:

using System.Net.Security;
using System.Security.Cryptography.X509Certificates;

var config = new HttpServerConnectionConfig
{
    ServerUrl = "https://localhost:5051",
    ApiKey = "dev-api-key",

    // WARNING: Only use in development!
    ServerCertificateCustomValidation = (request, cert, chain, errors) =>
    {
        // Accept any certificate (INSECURE - development only!)
        return true;
    }
};

using var connection = new HttpServerConnection(config);

Security Warning: Never use ServerCertificateCustomValidation = (_, _, _, _) => true in production!

Example 3: Custom Certificate Authority

For servers using internal/corporate CA:

var config = new HttpServerConnectionConfig
{
    ServerUrl = "https://internal-mcp.corp.local",
    ApiKey = "api-key",

    ServerCertificateCustomValidation = (request, cert, chain, errors) =>
    {
        if (errors == SslPolicyErrors.None)
            return true;

        // Custom CA validation
        if (errors.HasFlag(SslPolicyErrors.RemoteCertificateChainErrors))
        {
            // Load corporate CA certificate
            var corpCaCert = new X509Certificate2("corp-ca.crt");

            // Build chain with custom CA
            var customChain = new X509Chain();
            customChain.ChainPolicy.ExtraStore.Add(corpCaCert);
            customChain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority;

            bool isValid = customChain.Build(cert);
            customChain.Dispose();

            return isValid;
        }

        return false;
    }
};

Example 4: Production Configuration

var config = new HttpServerConnectionConfig
{
    ServerUrl = "https://mcp.example.com",
    ApiKey = Environment.GetEnvironmentVariable("MCP_API_KEY"),
    Timeout = TimeSpan.FromSeconds(60),
    MaxConnectionsPerServer = 20,

    // Default certificate validation (recommended for production)
    ServerCertificateCustomValidation = null  // Use system trust store
};

using var connection = new HttpServerConnection(config);

Certificate Validation

Default Validation

By default, the client uses .NET's built-in certificate validation:

  1. Certificate is trusted (issued by known CA)
  2. Certificate is valid (not expired, not yet valid)
  3. Certificate matches hostname (CN or SAN matches server URL)

Custom Validation Scenarios

Scenario 1: Accept Specific Self-Signed Certificate

var expectedThumbprint = "A1B2C3D4E5F6...";  // Certificate thumbprint

ServerCertificateCustomValidation = (request, cert, chain, errors) =>
{
    if (cert == null)
        return false;

    // Verify thumbprint matches expected
    return cert.Thumbprint.Equals(expectedThumbprint, StringComparison.OrdinalIgnoreCase);
}

Scenario 2: Log and Validate

ServerCertificateCustomValidation = (request, cert, chain, errors) =>
{
    if (errors == SslPolicyErrors.None)
        return true;

    // Log validation errors
    Console.WriteLine($"Certificate validation error: {errors}");

    if (cert != null)
    {
        Console.WriteLine($"Subject: {cert.Subject}");
        Console.WriteLine($"Issuer: {cert.Issuer}");
        Console.WriteLine($"Valid from: {cert.NotBefore} to {cert.NotAfter}");
    }

    // Reject in production, accept in development
    return Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Development";
};

Scenario 3: Certificate Pinning

For high-security requirements, pin specific certificates:

private static readonly HashSet<string> PinnedCertificates = new()
{
    "A1B2C3D4E5F6...",  // Primary server cert
    "B2C3D4E5F6A1..."   // Backup server cert
};

ServerCertificateCustomValidation = (request, cert, chain, errors) =>
{
    if (cert == null)
        return false;

    // Only accept pinned certificates
    return PinnedCertificates.Contains(cert.Thumbprint);
};

Authentication

API Key Authentication

Method 1: Configuration Object

var config = new HttpServerConnectionConfig
{
    ServerUrl = "https://mcp.example.com",
    ApiKey = "your-api-key"  // Sent as X-API-Key header
};

Method 2: Environment Variable

var config = new HttpServerConnectionConfig
{
    ServerUrl = "https://mcp.example.com",
    ApiKey = Environment.GetEnvironmentVariable("MCP_API_KEY")
};

Method 3: Azure Key Vault / Secrets Manager

// Example with Azure Key Vault
var keyVaultClient = new SecretClient(new Uri("https://your-vault.vault.azure.net"), new DefaultAzureCredential());
var apiKeySecret = await keyVaultClient.GetSecretAsync("mcp-api-key");

var config = new HttpServerConnectionConfig
{
    ServerUrl = "https://mcp.example.com",
    ApiKey = apiKeySecret.Value.Value
};

Mutual TLS (Client Certificates)

For bidirectional authentication:

// Load client certificate
var clientCert = new X509Certificate2("client-cert.pfx", "password");

var config = new HttpServerConnectionConfig
{
    ServerUrl = "https://mcp.example.com",
    ClientCertificate = clientCert,  // Present to server during TLS handshake
    ApiKey = "api-key"               // Additional API key authentication
};

using var connection = new HttpServerConnection(config);

Certificate Requirements:

  • Must include private key (PFX/PKCS#12 format)
  • Must be valid (not expired)
  • Server must be configured to accept client certificates

Error Handling

Common HTTPS Errors

Error 1: Certificate Validation Failed

System.Net.Http.HttpRequestException: The SSL connection could not be established
Inner Exception: The remote certificate is invalid according to the validation procedure.

Solution:

  1. Verify server certificate is valid
  2. Check certificate is issued by trusted CA
  3. Ensure hostname matches certificate CN/SAN
  4. For development, use custom validation (see examples above)

Error 2: Connection Timed Out

System.Threading.Tasks.TaskCanceledException: The request was canceled due to the configured HttpClient.Timeout

Solution:

  1. Increase timeout value
  2. Check network connectivity
  3. Verify firewall allows HTTPS traffic
var config = new HttpServerConnectionConfig
{
    Timeout = TimeSpan.FromSeconds(120)  // Increase timeout
};

Error 3: Hostname Mismatch

The remote certificate is invalid: the name on the certificate does not match the hostname

Solution:

  1. Use correct hostname in ServerUrl (match certificate CN/SAN)
  2. Update certificate to include correct hostname
  3. Use IP address if certificate has IP SAN

Error 4: Expired Certificate

The remote certificate is invalid: the certificate has expired

Solution:

  1. Renew server certificate
  2. For temporary workaround (development only):
ServerCertificateCustomValidation = (request, cert, chain, errors) =>
{
    // Check if only error is expiration
    if (errors == SslPolicyErrors.RemoteCertificateChainErrors)
    {
        return chain.ChainStatus.All(s => s.Status == X509ChainStatusFlags.NotTimeValid);
    }
    return errors == SslPolicyErrors.None;
};

Graceful Degradation

Handle HTTPS errors gracefully:

try
{
    using var connection = new HttpServerConnection(config);
    var tools = await connection.ListToolsAsync();
}
catch (HttpRequestException ex) when (ex.InnerException is System.Security.Authentication.AuthenticationException)
{
    // TLS/certificate error
    Console.WriteLine($"HTTPS connection failed: {ex.Message}");
    Console.WriteLine("Check server certificate configuration");
    // Optionally: retry with HTTP (if server supports it)
}
catch (TaskCanceledException ex)
{
    // Timeout
    Console.WriteLine($"Connection timed out after {config.Timeout.TotalSeconds}s");
    // Optionally: retry with longer timeout
}

Best Practices

1. Never Disable Certificate Validation in Production

// ❌ WRONG (insecure)
ServerCertificateCustomValidation = (_, _, _, _) => true;

// ✅ CORRECT (secure)
ServerCertificateCustomValidation = null;  // Use default validation

2. Store API Keys Securely

// ❌ WRONG (hardcoded)
ApiKey = "sk_live_1234567890abcdef";

// ✅ CORRECT (environment variable)
ApiKey = Environment.GetEnvironmentVariable("MCP_API_KEY");

// ✅ BETTER (secrets manager)
ApiKey = await secretsManager.GetSecretAsync("mcp-api-key");

3. Use Connection Pooling

// ❌ WRONG (creates new connection every time)
foreach (var request in requests)
{
    using var connection = new HttpServerConnection(config);
    await connection.ExecuteToolAsync("tool", args);
}

// ✅ CORRECT (reuse connection)
using var connection = new HttpServerConnection(config);
foreach (var request in requests)
{
    await connection.ExecuteToolAsync("tool", args);
}

4. Handle Errors Appropriately

try
{
    var result = await connection.ExecuteToolAsync("tool", args);
}
catch (HttpRequestException ex)
{
    // Log error, retry, or fail gracefully
    _logger.LogError(ex, "MCP tool execution failed");
    throw;
}

5. Configure Timeouts

// Short-lived operations
Timeout = TimeSpan.FromSeconds(10);

// Long-running operations
Timeout = TimeSpan.FromMinutes(5);

// Default (reasonable for most cases)
Timeout = TimeSpan.FromSeconds(30);

Troubleshooting

Issue: "The remote certificate is invalid"

Diagnosis:

# Check server certificate
echo | openssl s_client -connect mcp.example.com:443 -servername mcp.example.com 2>/dev/null | openssl x509 -noout -text

Common Causes:

  1. Self-signed certificate without custom validation
  2. Expired certificate
  3. Hostname mismatch
  4. Untrusted CA

Solution:

  • Ensure server has valid certificate from trusted CA
  • Or implement custom validation for development

Issue: Connection Hangs/Timeout

Diagnosis:

# Test connectivity
telnet mcp.example.com 443

# Test with curl
curl -v https://mcp.example.com/health

Common Causes:

  1. Firewall blocking port 443
  2. Server not responding
  3. Network connectivity issue

Solution:

  • Verify firewall rules
  • Check server is running
  • Increase timeout if server is slow

Issue: Client Certificate Not Accepted

Diagnosis: Check server logs for certificate validation errors.

Common Causes:

  1. Certificate expired
  2. Certificate not in PFX format
  3. Private key missing
  4. Server not configured for mutual TLS

Solution:

// Verify certificate has private key
var clientCert = new X509Certificate2("client-cert.pfx", "password");
Console.WriteLine($"Has private key: {clientCert.HasPrivateKey}");

// Check expiration
Console.WriteLine($"Valid from: {clientCert.NotBefore}");
Console.WriteLine($"Valid to: {clientCert.NotAfter}");

Issue: Slow HTTPS Connections

Diagnosis: Measure connection time:

var stopwatch = Stopwatch.StartNew();
using var connection = new HttpServerConnection(config);
stopwatch.Stop();
Console.WriteLine($"Connection established in {stopwatch.ElapsedMilliseconds}ms");

Common Causes:

  1. DNS resolution slow
  2. TLS handshake slow
  3. Server overloaded

Solution:

  • Use connection pooling (reuse connections)
  • Increase MaxConnectionsPerServer
  • Cache DNS (use IP address directly)

Configuration Examples

Development Environment

var config = new HttpServerConnectionConfig
{
    ServerUrl = "https://localhost:5051",
    ApiKey = "dev-api-key",
    Timeout = TimeSpan.FromSeconds(30),

    // Accept self-signed cert (development only)
    ServerCertificateCustomValidation = (request, cert, chain, errors) =>
    {
        if (errors == SslPolicyErrors.None)
            return true;

        // Log warning in development
        Console.WriteLine($"[DEV] Accepting certificate with errors: {errors}");
        return true;
    }
};

Staging Environment

var config = new HttpServerConnectionConfig
{
    ServerUrl = "https://staging-mcp.example.com",
    ApiKey = Environment.GetEnvironmentVariable("MCP_API_KEY_STAGING"),
    Timeout = TimeSpan.FromSeconds(60),
    MaxConnectionsPerServer = 10,

    // Validate cert but allow specific staging cert
    ServerCertificateCustomValidation = (request, cert, chain, errors) =>
    {
        if (errors == SslPolicyErrors.None)
            return true;

        // Accept specific staging certificate
        var stagingThumbprint = Environment.GetEnvironmentVariable("STAGING_CERT_THUMBPRINT");
        return cert?.Thumbprint == stagingThumbprint;
    }
};

Production Environment

var config = new HttpServerConnectionConfig
{
    ServerUrl = Environment.GetEnvironmentVariable("MCP_SERVER_URL"),
    ApiKey = await GetApiKeyFromSecretManagerAsync(),
    Timeout = TimeSpan.FromSeconds(120),
    MaxConnectionsPerServer = 50,

    // Use default certificate validation (strict)
    ServerCertificateCustomValidation = null
};

Security Checklist

Before deploying to production:

  • Server URL uses HTTPS (https://...)
  • Certificate validation is enabled (no custom bypass)
  • API key is stored securely (environment variable or secrets manager)
  • Timeout is configured appropriately for use case
  • Error handling is implemented
  • Connection is disposed properly (using statement)
  • Client certificate (if used) is stored securely
  • Certificate expiry monitoring is configured
  • Firewall allows outbound HTTPS (port 443)
  • TLS 1.2+ is enforced (no SSL3, TLS 1.0, TLS 1.1)

References

OpenHarbor.MCP Documentation:

.NET Documentation:

Security Resources:


Document Version: 1.0.0 Last Updated: 2025-10-19 Maintained By: Svrnty Development Team Related: Server HTTPS Setup