On this page
mTLS (mutual TLS) authentication for MCP servers
mTLS moves the credential out of the request and into the connection: the agent presents an X.509 certificate during the TLS handshake, so no header carries a secret.
- Method
- mTLS
- Platform
- Any platform
- MCP specification
- 2026-07-28
How it works
The certificate goes on the HTTP client the transport uses, never in the MCP payload: a tls.Config on an http.Client in Go, an ssl.SSLContext passed as verify in Python, an undici Agent behind a custom fetch in TypeScript. The request carries no credential — the server, or the proxy terminating TLS in front of it, knows the caller from the certificate that established the connection. That is also the limit: no chat or desktop MCP client config exposes a certificate field, so mTLS reaches servers from agents you write and nothing else.
- Best for
- Workload-to-workload calls where both ends are yours and a CA already issues certificates
- Specification
- Transport-layer authentication, outside the specification's OAuth profile — which the specification makes OPTIONAL
- Works with
- Your own agents only; no chat or desktop client config exposes a certificate field
- Effort
- An afternoon in code, longer if the PKI does not exist yet
The exchange
- Your agent holds cert and key
- MCP server or the proxy in front of it
-
The exchange happens once per connection, before any MCP message exists. A server that never sends CertificateRequest never sees a certificate, however well the client is configured.
Your agent to MCP server
TLS ClientHello SNI: mcp.example.com -
MCP server to Your agent
CertificateRequest acceptable CAs -
Your agent to MCP server
Certificate, CertificateVerify (carries the credential) CN=orders-agent, issued by your CA -
Inside MCP server
Verify the chain, then read the subject -
Every message on this connection is already authenticated, so a request copied out of a log carries nothing to replay. The cost is that nothing in MCP advertises the requirement, so you configure each caller by hand.
Your agent to MCP server
POST /mcp tools/list — no Authorization header -
MCP server to Your agent
200 OK tools listed
Connect your agent
Pick your language and SDK.
When to use something else
Use this when
- Both ends are workloads you run and a CA already issues their certificates, so there is no shared secret in an environment variable.
- A network or compliance rule requires a client certificate on every connection, and TLS terminates in a proxy or mesh you control.
- You want the credential bound to the connection rather than to a header, so nothing replayable appears in a request or a log.
Use something else when
- A person has to reach this server from a chat client. Claude, ChatGPT, Cursor, and VS Code can't present a certificate you supply, so mTLS is the wrong answer — check this before anything else.
- The agent calls on behalf of a named user. A certificate identifies a workload, and per-user identity needs the specification's OAuth flow.
- Nobody owns rotation. Expiry fails the handshake, so every agent stops at once and no MCP error says why.
Authorization is OPTIONAL, and HTTP-based transports SHOULD rather than MUST conform to the specification's OAuth profile, so a client certificate sits outside the specification rather than in conflict with it. Both live revisions say that, and neither one's standards list mentions TLS client authentication. The 2026-07-28 list runs to ten entries — OAuth 2.1, RFC 9728, RFC 8707, RFC 6750, RFC 8414, RFC 7591, RFC 9207, Client ID Metadata Documents, OpenID Connect Discovery 1.0, and OpenID Connect Dynamic Client Registration 1.0 — and none of them is this, which is why no client can discover the requirement.
A certificate says which workload opened this connection, and only for workloads you run; what arrives next is about people, tools, and what the server does with that identity.
- Step 1.
Add the second door
mTLS stays exactly as it is for the workloads you run. The gateway is a second route, for the callers that cannot present a certificate at all — every chat and desktop client.
"url": "https://
api. example. com/ mcp" - Step 2.
Add the policies
A list on the route, and the options that go with it. The gateway authenticates the caller its own way, then presents the client certificate your server already expects — gateway-to-origin mTLS, an Enterprise add-on.
"inbound": [ "mcp-oauth-inbound", "mcp-capability-filter-inbound", "mcp-token-exchange-inbound" ]
- Step 3.
Deploy
Your PKI, your CA, and your certificate-bearing workloads are untouched. The new route runs beside them.
zuplo deploy
-
Six analysts need two of these tools in Claude. They cannot install a certificate.
-
mcp-oauth-inboundThey sign in instead. The same server gets an OAuth door — a policy on the route, against the identity provider you already run — while your certificate-bearing workloads keep theirs. One MCP OAuth policy per project.
-
The certificate proves it is our agent. Which tools can our agent call?
-
mcp-capability-filter-inboundExactly the ones you list — a narrower answer than the certificate's, which only ever said "our agent may connect". Anything else is refused with
MethodNotFoundbefore it reaches your server. -
Our TLS log has a subject and a timestamp. Which tool actually ran?
-
capability_invocationThe event stream says: every tool call carries the caller, the tool, the upstream, the outcome, and the latency — per call, where a TLS log stops at the connection.
-
The API behind the server needs its own credential. Do the agents hold that too?
-
mcp-token-exchange-inboundNo — the gateway holds it and presents it per call; the agents never see it. Forwarding what a caller sent is the one thing the specification forbids with a MUST NOT.
All of these attach to one MCP route's policies.inbound — the same policy engine on the way in and on the way out. Your MCP server keeps
the code and the authentication it has today.
Common questions
Can Claude, ChatGPT, or Cursor connect to a server that requires a client certificate?
No. Claude's connector documentation lists six supported authentication types — OAuth with DCR, OAuth with a Client ID Metadata Document, OAuth with an Anthropic-held client ID and secret (a pre-registered client, not the client_credentials grant, which Claude does not support), custom connection, static headers in beta, and none — and a certificate is not among them. Cursor documents url, headers, and auth for a remote server; VS Code documents type, url, headers, and oauth. OpenAI states that ChatGPT cannot present customer-provided mTLS certificates.
Doesn't ChatGPT already use mTLS with MCP servers?
Yes, in the other direction. OpenAI documents that ChatGPT presents an OpenAI-managed client certificate when establishing TLS connections to MCP servers, and tells you to verify that the leaf certificate's SAN dnsName is mtls.prod.connectors.openai.com. That proves the caller is OpenAI's infrastructure rather than your user, and you can't substitute your own certificate. OAuth 2.1 still authenticates the person.
Claude Code documents CLAUDE_CODE_CLIENT_CERT. Does that cover MCP servers?
Treat it as unverified for MCP. Claude Code documents CLAUDE_CODE_CLIENT_CERT and CLAUDE_CODE_CLIENT_KEY as enterprise network configuration, read at startup and re-read when settings change, and /status shows an mTLS client cert row once the files load. That page frames them as the connection to the API provider and never mentions MCP, and the documented fields for an HTTP MCP server entry — type, url, headers, headersHelper, timeout, alwaysLoad — include no certificate. Test it against your own server before designing around it.
Does mTLS violate the MCP specification?
No. Authorization is OPTIONAL, and HTTP transports SHOULD rather than MUST conform to the OAuth profile, so transport-layer client authentication is out of scope rather than in conflict. What you give up is discovery: nothing advertises that a server expects a certificate, so you configure each caller directly.
Which specification revision applies here?
2026-07-28, the current protocol version, and for mTLS it makes no difference: neither it nor 2025-11-25 describes client certificates, and both make authorization OPTIONAL.
Is client certificate authentication the same thing as mTLS?
Yes. mTLS, mutual TLS, two-way SSL, and X.509 client certificate authentication all name the same handshake. It never touches the MCP protocol, so support depends only on whether your SDK lets you supply the HTTP client — which the Go, Python, and TypeScript SDKs all do.
Can mTLS and OAuth be combined?
They sit at different layers, so yes: terminate mTLS at the edge and run the specification's OAuth flow behind it. RFC 8705 goes further and binds an access token to the client's certificate, but the MCP authorization specification's standards list doesn't include RFC 8705, so nothing in MCP asks a client to produce a certificate-bound token.
What happens when a certificate expires?
The handshake fails before any MCP message exists. The specification's error table covers 401, 403, and 400, and a TLS failure becomes none of them, so agents report a transport error and the reason lands in your server's TLS log rather than in the protocol. Short-lived certificates need automated renewal on both ends.
One policy engine for APIs, AI, and MCP
Put your MCP servers behind a gateway that speaks every identity provider, filters tools per role, and logs every call.