Serve a valid TLS certificate for MCP
What we checked
We opened a TLS connection to your endpoint and inspected the certificate it presented and the protocol it negotiated:
Why it matters
TLS is what makes the connection to your server private and tamper-evident. A client that can’t verify your certificate has no way to know it’s really talking to you and not an attacker on the path, so a broken certificate undermines every other security property the endpoint claims. A weak key or an obsolete protocol version leaves the channel open to attacks that modern TLS closed years ago. A valid certificate is the foundation the rest of your transport security builds on: it’s what lets you safely enforce HTTPS and then advertise HSTS so clients never fall back to plaintext.
Does a broken certificate cap my score?
Yes, but only while it’s broken. Those failures hold the endpoint score to a ceiling rather than docking a few points. Reissue or reconfigure the certificate and the cap lifts on our next crawl, with no lasting penalty.
How to fix it
Serve a certificate that is CA-issued (e.g. Let’s Encrypt), matches the hostname clients connect to, and uses a strong key (RSA 2048-bit or larger, or ECDSA P-256). Serve it over TLS 1.2 or higher (prefer TLS 1.3); see Mozilla’s TLS configuration guidance. Make sure the full chain (leaf plus intermediates) is served, which is what most “untrusted” errors actually are.
# Obtain a free, auto-renewing Let's Encrypt certificate.
# Certbot generates a strong key and installs the full chain for you.
sudo certbot --nginx -d mcp.example.com
# Renewal is handled by a systemd timer / cron job; test it with:
sudo certbot renew --dry-run # Caddyfile - Caddy provisions and renews a trusted cert
# automatically and serves only TLS 1.2+ by default.
mcp.example.com {
reverse_proxy localhost:3000
} server {
listen 443 ssl;
server_name mcp.example.com;
# Serve the FULL chain (leaf + intermediates) to avoid "untrusted".
ssl_certificate /etc/letsencrypt/live/mcp.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/mcp.example.com/privkey.pem;
# Require modern protocols only.
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
location /mcp {
proxy_pass http://localhost:3000;
}
} How we re-check
We re-open the TLS connection on our next crawl. Once the certificate is CA-trusted, in-date, hostname-matching, backed by a strong key, and negotiates TLS 1.2+, the check passes on the following score refresh.
Give this to your AI
Paste this into Claude Code (or any coding agent) from inside your server's repository. It states the failing signal, the outcome we re-check for, and the format the fix has to take.
Context: this repository's infrastructure serves a remote MCP server over HTTPS. VerifyMCP's TLS check failed (an expired certificate, a hostname mismatch, a weak key, an outdated protocol, an untrusted chain, or an unreachable endpoint), which caps the endpoint score because a client cannot verify it is really talking to this server. Goal: the endpoint serves a CA-issued, in-date, hostname-matching certificate with a strong key (RSA 2048-bit+ or ECDSA P-256), negotiates TLS 1.2 or higher, and presents the full certificate chain, so the next crawl records a valid certificate. Do this: 1. Identify which failure fired and match the fix: reissue an expired certificate, correct the SAN/hostname on the cert, regenerate a weak key, raise the minimum negotiated protocol, or add the missing intermediate certificates to the served chain. 2. Use a CA that auto-renews (e.g. Let's Encrypt via certbot, or a proxy like Caddy) rather than a manually tracked expiry date. 3. Configure the TLS terminator (nginx, Caddy, load balancer) to serve TLSv1.2 and TLSv1.3 only, and to serve the leaf certificate together with all intermediates. 4. Verify with `openssl s_client -connect <host>:443 -servername <host>` that the chain, hostname, and negotiated protocol are all correct. Rules: - Never self-sign a certificate or bypass validation to make the check pass; the certificate has to be genuinely CA-trusted. - If raising the minimum TLS version would drop support for TLS 1.0/1.1 clients still connecting to this endpoint, check whether that traffic exists and ask the human before cutting it off. TLS 1.2+ is still the correct end state, so treat any remaining 1.0/1.1 traffic as a migration to schedule, not an outcome to preserve. - This is production infrastructure: confirm the change in staging or a maintenance window before touching the live certificate. Report back: the failure you fixed, and the `openssl s_client` output (or external TLS report) showing a trusted chain, matching hostname, and TLS 1.2+ negotiation. Reference: https://verifymcp.io/docs/remote/tls