I'm running mcp-mail-server as a stdio MCP server under Claude Desktop on Windows 11 (Node v24.18.0). Every send fails with:
SMTP connection failed: unable to verify the first certificate
Webmail for the same account works fine in a browser.
Inspecting the chain with a small script (certcheck.js):
const tls = require('tls');
const s = tls.connect(
{ host: 'mail.example.com', port: 465, servername: 'mail.example.com', rejectUnauthorized: false },
() => {
console.log('authorized:', s.authorized);
console.log('authorizationError:', s.authorizationError);
let c = s.getPeerCertificate(true), seen = new Set();
while (c && c.subject && !seen.has(c.fingerprint)) {
seen.add(c.fingerprint);
console.log('---\n subject:', JSON.stringify(c.subject), '\n issuer :', JSON.stringify(c.issuer));
c = (c.issuerCertificate && c.issuerCertificate.fingerprint !== c.fingerprint) ? c.issuerCertificate : null;
}
s.end();
}
);
s.on('error', e => console.error('error:', e.message));
gives:
authorized: false
authorizationError: SELF_SIGNED_CERT_IN_CHAIN
---
subject: {"CN":"webmail.example.com"}
issuer : {"OU":"generated by Norton Antivirus for SSL/TLS scanning","O":"Norton Web/Mail Shield","CN":"Norton Web/Mail Shield Root"}
---
subject: {"OU":"generated by Norton Antivirus for SSL/TLS scanning","O":"Norton Web/Mail Shield","CN":"Norton Web/Mail Shield Root"}
issuer : {"OU":"generated by Norton Antivirus for SSL/TLS scanning","O":"Norton Web/Mail Shield","CN":"Norton Web/Mail Shield Root"}
So Norton is intercepting the connection and substituting its own self-signed root. That root is in the Windows certificate store, which is why the browser is happy and Node isn't.
Things I tried, none of which changed the connector's behaviour:
NODE_USE_SYSTEM_CA=1(supported on Node >= 22.19 / 23.8 / 24.6) — givesauthorized: truein a shell, no effect on the serverNODE_EXTRA_CA_CERTSpointing at the exported Norton root — alsoauthorized: truein a shell, no effect on the server- setting both in the MCP
envblock, viasetx, and injected inside thecmdwrapper (set VAR=1&& npx ...), confirming withGet-CimInstance Win32_Processthat the spawned process genuinely had them - turning Norton Auto-Protect and Firewall off — interception persists, the issuer is unchanged
Why do Node's CA environment variables have no effect, and what's the correct fix?