Skip to content

Enterprise MCP registry unreachable on macOS: rustls rejects private CA cert (Apple -67901), fail-closed blocks all MCP #4364

Description

@jlandure

Describe the bug

On macOS, Copilot CLI 1.0.78 fails to validate MCP servers against an enterprise custom MCP registry because TLS verification rejects the registry's private-CA certificate with Apple error -67901 (certificate is not standards compliant).

The CLI then treats the registry as unreachable and fail-closes: all non-default / custom MCP servers are reported as blocked by policy, even though:

  • the registry is reachable over HTTPS from the same machine (curl / Node fetch return HTTP 200)
  • Chrome trusts and displays the certificate without issue
  • the same servers are listed in the enterprise registry and were usable with an earlier CLI version (allowlist check previously went through the Node/JS path)

This looks like a regression from moving the MCP registry allowlist HTTP client into the Rust runtime (reqwest + rustls-platform-verifier / Apple Security.framework), which applies stricter Apple TLS rules than Node/Chrome.

Affected version

  • GitHub Copilot CLI 1.0.78 (auto-updated ~2026-08-03 / 2026-08-04)
  • macOS 26.x (Apple Silicon)
  • Enterprise / org Copilot policy: custom MCP Registry URL + Registry only allowlist

Previously worked on an earlier 1.x CLI where registry allowlist verification still lived in the JS/app.js path (Node TLS). After 1.0.78, the same check appears in the native runtime (rustls_platform_verifier::verification::apple) and fails.

Steps to reproduce

  1. Configure an org/enterprise Copilot MCP registry URL pointing at an internal HTTPS registry (private PKI), with Restrict MCP access to registry servers = Registry only.
  2. Add one or more registry-listed MCP servers to ~/.copilot/mcp-config.json (or via /mcp add).
  3. Start Copilot CLI with --log-level debug (or run /mcp reload).
  4. Observe custom MCP servers blocked by policy.

Expected behavior

  • Registry HTTPS fetch should succeed for a certificate that the OS / Chrome / Node already trust for that host, or
  • If Apple TLS policy rejects the leaf (e.g. validity period), the CLI should surface a clear TLS/certificate error (not a generic “blocked by policy”), and ideally provide a documented enterprise path (fail-open option, private-CA guidance, or less strict verification for admin-configured registry URLs).

Actual behavior

User-facing:

! 3 MCP servers were blocked by policy: '<server-a>', '<server-b>', '<server-c>'

Debug log (redacted host):

[DEBUG] Registry https://<REDACTED-INTERNAL-REGISTRY>/: servers will be verified against this registry
[DEBUG] starting new connection 'Some("<REDACTED-INTERNAL-REGISTRY>")'
[ERROR] failed to verify TLS certificate: invalid peer certificate: Other(OtherError("“*.<REDACTED-INTERNAL-DOMAIN>” certificate is not standards compliant: -67901"))
        log.target=rustls_platform_verifier::verification::apple
[DEBUG] Registry https://<REDACTED-INTERNAL-REGISTRY>/ unreachable when checking server "<server-id>" after 3 attempts:
        error sending request for url (https://<REDACTED-INTERNAL-REGISTRY>/v0.1/servers/<server-id>/versions/latest)
[ERROR] MCP server "<server-id>" filtered: Registry https://<REDACTED-INTERNAL-REGISTRY>/ was unreachable

Telemetry kinds observed: mcp_policy_check, mcp_allowlist_policy_check.

Same host from the same machine:

  • curl → TLS OK, HTTP 200 on /v0/servers and /v0.1/servers
  • Node fetch → OK
  • Chrome certificate viewer → trusts leaf issued by internal CA

Certificate details (redacted)

  • Subject CN / SAN: *.<REDACTED-INTERNAL-DOMAIN>
  • Issuer: internal enterprise PKI (private CA)
  • Validity window: ~1095 days (issued Jan 2026 → expires Jan 2029)
  • Apple trusted-certificate guidance requires leaf validity ≤ 825 days for certs issued after 2019 — this is the likely trigger for -67901 / “not standards compliant”
  • Fingerprint available privately if needed; hostnames intentionally redacted

Additional context / analysis

Local package comparison on disk:

CLI package Allowlist / registry verify strings TLS stack for that check
1.0.58 / 1.0.59 / 1.0.61 Present in app.js Node
1.0.78 Present in runtime.node, removed from app.js Rust + rustls_platform_verifier (Apple)

So this is not “registry down” and not (yet) an identity fingerprint mismatch (does not match the registered server identity). The allowlist never reaches a successful registry HTTP response.

Related but distinct issues: #2481 / #2498 (policy fetch 404), #3934 (identity mismatch), #4346 (403 in CI). This report is specifically TLS verification of the enterprise registry URL on macOS after the Rust migration.

Asks

  1. Confirm whether enterprise MCP registry fetches are intentionally pinned to rustls-platform-verifier on macOS.
  2. Improve error UX: distinguish TLS/cert rejection from policy deny / true unreachability.
  3. Consider enterprise-friendly options: honor OS trust more like Chrome for admin-configured registry URLs, document Apple 825-day constraint for private PKI, or provide a supported workaround short of reissuing certs / switching policy to Allow all.
  4. Optionally fail-open (or cache last-good registry allowlist) when the configured registry URL fails TLS, similar to other managed-settings fail-open work in 1.0.78 — today MCP custom servers are hard-blocked.

Happy to provide more info if needed!

Activity

  1. added
    area:enterpriseGitHub Enterprise (GHE/GHES) support, org policies, and enterprise settings
    area:mcpMCP server configuration, discovery, connectivity, OAuth, policy, and registry
    area:networkingProxy, SSL/TLS, certificates, corporate environments, and connectivity issues
    on Aug 11, 2026
  2. nicodemuz commented on Sep 29, 2026

    @nicodemuz

    I'm encountering the same issue. Bug report by Copilot CLI below:

    Enterprise MCP registry unreachable due to TLS 1.2-only server + rustls TLS 1.3 requirement → CLI fails closed and blocks all custom MCP servers

    Summary

    Our organization enforces registry_access: "registry_only" via an Azure API Center-hosted MCP registry. The registry endpoint only supports TLS 1.2. Since Copilot CLI moved MCP registry validation to the native Rust HTTP client (reqwest + rustls/rustls-platform-verifier), starting around v1.0.78, the registry handshake fails outright (TLS 1.3 is required/preferred by the client), and the CLI fails closed — blocking all custom MCP servers, even ones that are correctly listed in the registry and would otherwise connect successfully.

    This looks like the same class of bug as #4364 (rustls being stricter than the previous Node/JS TLS stack, causing a working registry to be treated as unreachable), but triggered by TLS protocol version mismatch rather than private CA cert trust. Filing this as a related report / additional data point in case it's a distinct root cause under the same symptom.

    Environment

    • Copilot CLI version: 1.0.89 (also reproduced on 1.0.85–1.0.88)
    • OS: macOS (Apple Silicon, arm64)
    • Install method: standalone self-updating binary (~/.local/bin/copilot), not Homebrew/npm
    • Org MCP registry: Azure API Center-hosted, registry_access: "registry_only" policy (confirmed via gh api /copilot/mcp_registry)
    • Registry host: <redacted>.data.<region>.azure-apicenter.ms (Azure API Center MCP registry, workspace default)

    Steps to Reproduce

    1. Configure an enterprise/org Copilot policy with registry_access: "registry_only" pointing to an Azure API Center MCP registry that only supports TLS 1.2 (no TLS 1.3 listener/cipher suite configured).
    2. Add one or more MCP servers locally in ~/.copilot/mcp-config.json that are also present in the registry (e.g. an http-type remote server).
    3. Launch copilot.
    4. Observe the startup banner:
      2 MCP servers were blocked by policy: '<server-a>', '<server-b>'
      
    5. Run /mcp show → Online tab. Observe:
      Failed to load online results: Error: HTTP request failed: error sending request for url
      (https://<registry-host>/workspaces/default/v0.1/servers?limit=30)
      

    Root Cause Analysis

    Verified directly with openssl s_client against the registry host:

    $ openssl s_client -connect <registry-host>:443 -tls1_3
    ...
    SSL alert number 40
    140704... SSL routines:ssl3_read_bytes:sslv3 alert handshake failure
    
    $ openssl s_client -connect <registry-host>:443 -tls1_2
    ...
    New, TLSv1.2, Cipher is ECDHE-RSA-AES256-GCM-SHA384
    Verify return code: 0 (ok)
    

    The registry server only accepts TLS 1.2.

    curl against the same URL succeeds (HTTP 200, valid JSON) from multiple machines/networks, because curl's TLS stack negotiates down to TLS 1.2 transparently. This makes the issue easy to misdiagnose as a network/proxy/DNS/RBAC problem, since manual connectivity checks all look "clean."

    The Copilot CLI's registry-fetch request (used both for the startup policy check and the /mcp show → Online tab) does not appear to negotiate down the same way, and the request fails. The CLI then fails closed, blocking all locally configured MCP servers that aren't already trusted/cached — rather than surfacing a TLS/connectivity error or failing open.

    Expected Behavior

    • The CLI's HTTP client should support the same TLS version range as common HTTP clients (curl, browsers), i.e. negotiate TLS 1.2 when the server doesn't support 1.3, rather than hard-failing.
    • If the registry truly cannot be reached, the CLI should surface a clear, actionable error (e.g. "Failed to reach MCP registry: TLS handshake failed") instead of a generic "blocked by policy" banner that gives no indication of the underlying transport failure.
    • Ideally, failure to validate against the registry should not silently block servers that are otherwise configured correctly and reachable — or at minimum should be distinguishable from an explicit deny-by-policy block.

    Additional Notes

    • Both blocked servers in our case (atlassian-mcp-v2-auth, a remote http-type server, and playwright, a local npx-launched server) actually connect and authenticate successfully at the transport level per session logs (Successfully authenticated with ..., Service initialized as client), despite being reported as "blocked by policy" in the startup banner. This suggests the banner/registry-check failure is somewhat decoupled from actual server connectivity, but still degrades the UX and (per /mcp show) prevents browsing the org's allowed server catalog.
    • We are attempting to verify whether this regression was introduced around v1.0.78 (when registry validation reportedly moved from a Node/JS-based TLS client to the native Rust/reqwest+rustls client) by testing against an older CLI build; will update this issue with results.
    • Workaround considered: none currently available on the client side; the real fix is likely either (a) enabling TLS 1.3 on the registry endpoint (Azure API Center-side, may not be configurable by us), or (b) the CLI's HTTP client negotiating TLS 1.2 as a fallback like other clients do.

    Ask

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:enterpriseGitHub Enterprise (GHE/GHES) support, org policies, and enterprise settingsarea:mcpMCP server configuration, discovery, connectivity, OAuth, policy, and registryarea:networkingProxy, SSL/TLS, certificates, corporate environments, and connectivity issues

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions