- 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>
17 KiB
HTTPS/TLS Setup Guide - Svrnty.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
- Overview
- Prerequisites
- Client Configuration
- Certificate Validation
- Authentication
- Error Handling
- Best Practices
- Troubleshooting
Overview
Svrnty.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 Svrnty.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:
- Certificate is trusted (issued by known CA)
- Certificate is valid (not expired, not yet valid)
- 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:
- Verify server certificate is valid
- Check certificate is issued by trusted CA
- Ensure hostname matches certificate CN/SAN
- 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:
- Increase timeout value
- Check network connectivity
- 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:
- Use correct hostname in ServerUrl (match certificate CN/SAN)
- Update certificate to include correct hostname
- Use IP address if certificate has IP SAN
Error 4: Expired Certificate
The remote certificate is invalid: the certificate has expired
Solution:
- Renew server certificate
- 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:
- Self-signed certificate without custom validation
- Expired certificate
- Hostname mismatch
- 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:
- Firewall blocking port 443
- Server not responding
- 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:
- Certificate expired
- Certificate not in PFX format
- Private key missing
- 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:
- DNS resolution slow
- TLS handshake slow
- 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
Svrnty.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