[BUG] MCP client ignores MacOS keychain client secret, only uses value of MCP_CLIENT_SECRET environment variable

Resolved 💬 4 comments Opened Apr 21, 2026 by nbrahms Closed Apr 22, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

When adding an HTTP MCP server with --client-secret, Claude Code correctly stores the client secret in the macOS keychain under "Claude Code-credentials". However, during the OAuth token exchange, the client_secret parameter is not included in the POST to the authorization server's /token endpoint, causing the exchange to fail.

The secret is present in the keychain but never retrieved at runtime.

This is for an oauth server with a ASM document with content:

{
  "issuer": "<host>",
  "authorization_endpoint": "<host>/authorize",
  "token_endpoint": "<host>/token",
  "jwks_uri": "<host>/.well-known/jwks.json",
  "token_endpoint_auth_methods_supported": [
     "client_secret_post"
  ],
  "grant_types_supported": [
     "authorization_code"
  ],
  "code_challenge_methods_supported": [
     "S256"
  ],
  "response_types_supported": [
     "code"
  ]
}

Note that this OAuth server does not support DCR.

What Should Happen?

The client_secret stored in the keychain is retrieved and included as client_secret in the POST body of the token exchange request, satisfying client_secret_post authentication.

Error Messages/Logs

# Log from oauth server

[token] request {
  grant_type: 'authorization_code',
  client_id: '<client id>',
  redirect_uri: '<callback uri>',
  has_code: true,
  has_verifier: true,
  has_secret: false
}

Steps to Reproduce

  1. Add an HTTP MCP server that requires client_secret_post OAuth authentication:

``
MCP_CLIENT_SECRET="my-secret" claude mcp add-json my-server \
'{"type":"http","url":"http://localhost:abc/","oauth":{"clientId":"my-client-id"}}' \
--client-secret
``

  1. Confirm the secret was stored — it appears in the keychain:

``
security find-generic-password -s "Claude Code-credentials" -w
# Returns JSON containing:
# { "mcpOAuthClientConfig": { "my-server|<hash>": { "clientSecret": "..." } } }
``

  1. Start Claude Code and trigger the OAuth flow via /mcp → Authenticate.
  2. Complete the browser-based authorization and observe the token exchange request on the authorization server. The client_secret field is absent from the POST body.

The token exchange POST is sent without client_secret. Authorization servers enforcing client_secret_post reject the request with invalid_request / missing required parameters.

Environment

  • macOS (Darwin 25.4.0)
  • Claude Code (claude-sonnet-4-6)
  • Transport: HTTP with OAuth (client_secret_post)

Claude Model

Sonnet (default)

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

2.1.116

Platform

Anthropic API

Operating System

macOS

Terminal/Shell

iTerm2

Additional Information

Workaround

Setting MCP_CLIENT_SECRET in the shell environment at Claude Code launch time works:
MCP_CLIENT_SECRET="my-secret" claude
or via ~/.zprofile:
export MCP_CLIENT_SECRET="my-secret"

However this is a single global variable, so it cannot support multiple MCP servers with different client secrets simultaneously.

View original on GitHub ↗

This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗