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

691 lines
17 KiB
Markdown

# 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](#overview)
2. [Prerequisites](#prerequisites)
3. [Client Configuration](#client-configuration)
4. [Certificate Validation](#certificate-validation)
5. [Authentication](#authentication)
6. [Error Handling](#error-handling)
7. [Best Practices](#best-practices)
8. [Troubleshooting](#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**
```csharp
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:**
```csharp
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:
```csharp
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:
```csharp
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
```csharp
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**
```csharp
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**
```csharp
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:
```csharp
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**
```csharp
var config = new HttpServerConnectionConfig
{
ServerUrl = "https://mcp.example.com",
ApiKey = "your-api-key" // Sent as X-API-Key header
};
```
**Method 2: Environment Variable**
```csharp
var config = new HttpServerConnectionConfig
{
ServerUrl = "https://mcp.example.com",
ApiKey = Environment.GetEnvironmentVariable("MCP_API_KEY")
};
```
**Method 3: Azure Key Vault / Secrets Manager**
```csharp
// 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:
```csharp
// 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
```csharp
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):
```csharp
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:
```csharp
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
```csharp
// ❌ WRONG (insecure)
ServerCertificateCustomValidation = (_, _, _, _) => true;
// ✅ CORRECT (secure)
ServerCertificateCustomValidation = null; // Use default validation
```
### 2. Store API Keys Securely
```csharp
// ❌ 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
```csharp
// ❌ 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
```csharp
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
```csharp
// 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:**
```bash
# 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:**
```bash
# 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:**
```csharp
// 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:
```csharp
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
```csharp
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
```csharp
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
```csharp
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:**
- [Client README](../../README.md)
- [API Reference](../../docs/api/client-api.md)
- [Integration Guide](../../docs/integration-guide.md)
**.NET Documentation:**
- [HttpClient Security](https://learn.microsoft.com/en-us/dotnet/api/system.net.http.httpclient)
- [X509 Certificates](https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.x509certificates.x509certificate2)
- [SSL/TLS Best Practices](https://learn.microsoft.com/en-us/dotnet/framework/network-programming/tls)
**Security Resources:**
- [OWASP TLS Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Transport_Layer_Protection_Cheat_Sheet.html)
- [Certificate Pinning Guide](https://owasp.org/www-community/controls/Certificate_and_Public_Key_Pinning)
---
**Document Version**: 1.0.0
**Last Updated**: 2025-10-19
**Maintained By**: Svrnty Development Team
**Related**: [Server HTTPS Setup](../../OpenHarbor.MCP.Server/docs/deployment/https-setup.md)