[BUG] MCP client ignores MacOS keychain client secret, only uses value of MCP_CLIENT_SECRET environment variable
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
- 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
- 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": "..." } } }
- Start Claude Code and trigger the OAuth flow via /mcp → Authenticate.
- 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.
This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗