Learn Claude Code: MCP Mastery OAuth Authentication and Dynamic Headers

OAuth Authentication and Dynamic Headers

Advanced 🕐 15 min Lesson 5 of 12
What you'll learn
  • Configure OAuth 2.0 authentication for remote MCP servers — from the standard browser flow through pre-configured credentials and headless environments
  • Use headersHelper to generate dynamic tokens for Kerberos, SSO, and short-lived credential schemes, and understand its workspace trust requirement
  • Identify which Anthropic-hosted connectors require authenticating through claude.ai rather than locally, and why

Where most MCP setups stall: authentication

Adding an MCP server is one command. Authenticating it is where developers stall. Remote services use a range of authentication schemes — standard OAuth flows, Bearer tokens, internal SSO systems, short-lived certificates — and each requires a different approach. Getting the configuration right the first time saves a lot of debugging sessions that eventually trace back to a stored credential that expired, a redirect URI that was never registered, or a token that a shared config file exposed to the whole team.

How Claude Code detects servers that need authentication

Claude Code marks a remote server as needing authentication when the server responds with 401 Unauthorized or 403 Forbidden during the initial connection. Either response flags the server in /mcp as "Needs authentication." Starting from v2.1.193, Claude Code also shows a startup notice when one or more configured servers need sign-in, so you see the problem immediately rather than discovering it when Claude tries to use a tool.

When a stored refresh token is rejected by the server — meaning the token expired or was revoked — Claude Code immediately shows a notice pointing at /mcp, where the server's menu offers a Re-authenticate option. You sign in again without losing your session.

The standard OAuth 2.0 flow

For servers that support OAuth, the flow is: add the server, then open /mcp and choose Authenticate. A browser window opens to the service's sign-in page. After you approve the connection, Claude Code stores the access token and refresh token securely in your system keychain (macOS) or a credentials file, and refreshes them automatically. You only sign in once per service.

claude mcp add --transport http sentry https://mcp.sentry.dev/mcp # Then inside a session: /mcp → select sentry → Authenticate

You can also run the OAuth flow directly from your shell without starting a session:

claude mcp login sentry

Authenticating in headless and SSH environments

In environments without a browser — SSH sessions, Linux without a display server, CI pipelines — the OAuth flow cannot open a browser window automatically. Claude Code detects this and prints the authorization URL instead. Open that URL on your local machine, complete the sign-in, then paste the full redirect URL from your browser's address bar back at the prompt. The SSH connection needs an interactive terminal for the paste step, so connect with ssh -t.

To force URL mode even when a local browser is detected:

claude mcp login sentry --no-browser

Pre-configured OAuth credentials

Some servers do not support Dynamic Client Registration and require you to register an OAuth application in their developer portal first. If you see the error "Incompatible auth server: does not support dynamic client registration," the server needs pre-configured credentials. Register an app, note the client ID and client secret, then add the server with those credentials:

claude mcp add --transport http --client-id your-id --client-secret --callback-port 8080 myserver https://mcp.example.com/mcp

The --client-secret flag prompts for the secret with masked input rather than taking it as a command-line argument, preventing it from appearing in shell history. The client secret is stored in your system keychain, not in the configuration file.

Use --callback-port to fix the OAuth redirect URI when the server requires a pre-registered redirect URL. The port you specify must match a redirect URI you registered in the server's developer portal in the form http://localhost:PORT/callback.

To restrict which OAuth scopes Claude Code requests — useful when an upstream server advertises more permissions than you need — set oauth.scopes in the server's .mcp.json entry as a space-separated string:

{"type": "http", "url": "https://mcp.slack.com/mcp", "oauth": {"scopes": "channels:read chat:write"}}

Static tokens and dynamic headers

For servers that authenticate with a static API key — GitHub's remote MCP server is a common example — pass the token as a header at add time:

claude mcp add --transport http github https://api.githubcopilot.com/mcp/ --header "Authorization: Bearer YOUR_GITHUB_PAT"

The token is stored in your configuration. This works well for personal API keys that do not expire. For short-lived tokens, SSO-issued credentials, Kerberos tickets, or any scheme where the token must be generated fresh at connection time, use headersHelper instead. headersHelper is a shell command that runs at each connection and whose JSON output is merged into the connection headers:

{"type": "http", "url": "https://mcp.internal.example.com", "headersHelper": "/opt/bin/get-mcp-auth.sh"}

The command must write a JSON object of string key-value pairs to stdout and complete within 10 seconds. Dynamic headers override any static headers with the same name. Claude Code sets CLAUDE_CODE_MCP_SERVER_NAME, CLAUDE_CODE_MCP_SERVER_URL, and CLAUDE_PLUGIN_ROOT in the helper's environment, so a single script can serve multiple servers by branching on the server name or URL.

If a tool call returns 401 or 403, Claude Code re-runs the helper, reconnects with fresh headers, and retries the call once automatically.

Services that require authenticating through claude.ai

Gmail, Google Calendar, Microsoft 365, and some other Anthropic-hosted connectors cannot be authenticated locally. The upstream identity provider — Google, Microsoft — only accepts the redirect URI that Anthropic registered at claude.ai, not the localhost callback that Claude Code's local OAuth flow would use.

Starting from v2.1.162, if you try to authenticate one of these services in /mcp, Claude Code shows a message directing you to connect it at Settings → Connectors on claude.ai instead. Once you connect the service there, it appears in Claude Code automatically — no local OAuth required. This is not a bug or a limitation to work around; it is the intended flow for these services.

headersHelper and workspace trust

headersHelper executes arbitrary shell commands. This gives it significant power — and significant risk if that command comes from an untrusted source. When headersHelper is defined at project or local scope, it only runs after the workspace trust dialog has been accepted. A repository you clone cannot trigger headersHelper execution before you have reviewed and trusted the project's configuration. This protects against a class of supply chain attacks where a malicious .mcp.json could otherwise extract credentials before you realize the repository is hostile.

Key takeaways
  • Claude Code marks a server as needing auth on 401 or 403 — open /mcp to authenticate, or run claude mcp login from your shell to complete the OAuth flow without starting a session
  • Use --no-browser with claude mcp login in SSH sessions — Claude Code detects missing display servers automatically and prints the authorization URL for manual completion
  • headersHelper generates fresh credentials at each connection from an arbitrary shell command — it only executes after the workspace trust dialog is accepted to prevent credential theft from untrusted repositories
  • Gmail, Google Calendar, and Microsoft 365 cannot authenticate locally because the identity provider only accepts the redirect URI registered at claude.ai — connect them at Settings → Connectors on claude.ai and they appear in Claude Code automatically
  • oauth.scopes pins the exact permission set requested during authorization — use it to restrict a connector to a security-approved subset when the upstream server advertises broader scopes than needed