Skip to content

Feature Request: Support SSL_CERT_FILE environment variable for custom CA certificates #3

Description

@btpka3

The following content is generated by ai agent :

Feature Request: Support SSL_CERT_FILE environment variable for custom CA certificates

Problem

mcp2cli uses hyper-rustls with webpki-roots which bundles the Mozilla CA set at compile time. This means the binary cannot trust any CA certificates outside that bundle at runtime.

In corporate environments that use MITM/TLS-inspection proxies (e.g. mitmproxy, ZScaler, Cloudflare WARP, Fortinet), all outbound HTTPS traffic gets re-signed with a corporate root CA. Since mcp2cli doesn't trust that CA, connections fail with:

Error: streamable HTTP request failed: client error (Connect)

This is a hard blocker for anyone behind such a proxy — the binary simply cannot connect to any MCP server over HTTPS.

Proposed Solution

Support the SSL_CERT_FILE environment variable — an industry-standard convention shared by curl, Python requests, Go net/http, Ruby OpenSSL, and many other tools. When set, mcp2cli should read additional CA certificates from the specified PEM file and add them to the root store alongside the bundled webpki roots.

Implementation

The change is minimal (~40 lines in src/mcp/client.rs + 3 lines in Cargo.toml):

  1. Add explicit rustls, rustls-pemfile, and webpki-roots dependencies to Cargo.toml
  2. In the HTTPS connector construction, build a RootCertStore manually:
    • Start with webpki_roots::TLS_SERVER_ROOTS (preserves current behavior)
    • If SSL_CERT_FILE is set, parse the PEM file and add any certificates found
    • Log via tracing::debug how many extra certs were loaded (or tracing::warn if the file can't be opened)
  3. Build rustls::ClientConfig with this store and pass it to HttpsConnectorBuilder::with_tls_config()

Verification

Tested on a sandboxed environment with mitmproxy transparent proxy:

Condition Result
Without SSL_CERT_FILE Error: streamable HTTP request failed: client error (Connect)
With SSL_CERT_FILE=/opt/opensandbox/mitmproxy-ca-cert.pem Connection succeeds, tool calls work normally

Diff

--- a/Cargo.toml
+++ b/Cargo.toml
@@ -54,6 +54,9 @@
 hyper-rustls = { version = "0.27", default-features = false, features = ["http1", "ring", "webpki-tokio", "tls12"] }
+rustls = { version = "0.23", default-features = false, features = ["ring", "std"] }
+rustls-pemfile = "2"
+webpki-roots = "1.0"

--- a/src/mcp/client.rs
+++ b/src/mcp/client.rs
@@ -56,6 +56,7 @@
+use rustls_pemfile;

@@ connector construction @@
-        let connector = hyper_rustls::HttpsConnectorBuilder::new()
-            .with_webpki_roots()
+        let mut root_store = rustls::RootCertStore::empty();
+        root_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
+
+        if let Ok(cert_file) = std::env::var("SSL_CERT_FILE") {
+            match std::fs::File::open(&cert_file) {
+                Ok(file) => {
+                    let mut reader = std::io::BufReader::new(file);
+                    let certs = rustls_pemfile::certs(&mut reader);
+                    let mut added = 0u32;
+                    for cert in certs.flatten() {
+                        if root_store.add(cert).is_ok() {
+                            added += 1;
+                        }
+                    }
+                    tracing::debug!("loaded {} extra CA cert(s) from SSL_CERT_FILE={}", added, cert_file);
+                }
+                Err(e) => {
+                    tracing::warn!("SSL_CERT_FILE={} set but could not open: {}", cert_file, e);
+                }
+            }
+        }
+
+        let tls_config = rustls::ClientConfig::builder()
+            .with_root_certificates(root_store)
+            .with_no_client_auth();
+        let connector = hyper_rustls::HttpsConnectorBuilder::new()
+            .with_tls_config(tls_config)
             .https_or_http()
             .enable_http1()
             .build();

Notes

  • No behavior change when SSL_CERT_FILE is unset — the bundled webpki roots are used exactly as before.
  • The env var name aligns with existing ecosystem conventions, minimizing user friction.
  • A future enhancement could also support SSL_CERT_DIR for directory-based trust stores.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions