[BUG] Removing whitespace from an MCP server's `url`, as the config warning suggests, loses its OAuth sign-in (token key is a hash of the raw URL)
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report
- [x] I am using the latest version of Claude Code
What's Wrong?
If an HTTP MCP server's url has leading or trailing whitespace, Claude Code warns about it:
claude mcp list:[Warning] [demo] mcpServers.demo: Leading or trailing whitespace in: urlclaude doctor: the same line, plusSuggested fix: Remove the whitespace from these values in the "demo" entry — they are used exactly as written
The server connects fine with the whitespace. If you follow the suggested fix, the server shows ! Needs authentication and you have to sign in again.
The cause is how the stored OAuth credentials are looked up. mcpOAuth entries in .credentials.json are keyed as <name>|<first 16 hex chars of sha256(JSON.stringify({ type, url, headers }))>, and that hash uses the URL exactly as written. Nothing trims it first. Removing the whitespace changes the key, so the existing token is no longer found. The old entry stays in the file, orphaned, and a new token-less entry is created under the new key. If you move the old entry to the new key by hand, the server connects again with the same token, without a new sign-in. The grant itself is still valid; only the lookup misses it.
Example keys for a server named demo:
| url as written | key |
|---|---|
| "https://example.com/mcp " | demo\|f6083a2b4b5762fa |
| "https://example.com/mcp" | demo\|9a70b85417749d5c |
You can get whitespace into the URL without noticing: claude mcp add --transport http demo "https://example.com/mcp " (for example, a pasted URL with a trailing space) stores the space as-is and prints no warning at that point.
What Should Happen?
Following the warning's own suggested fix should not sign the server out. Possible fixes:
- derive the credential key from the trimmed URL (connecting already tolerates the whitespace), or
- when the trimmed URL matches an existing entry's URL, move that entry to the new key on lookup, or
- at minimum, say in the warning and suggested fix that removing the whitespace means signing in to the server again via
/mcp.
Tying the token to the server URL is fine. The problem is that a difference Claude Code itself calls a mistake ends the sign-in.
Error Messages/Logs
$ claude mcp list # url is "https://example.com/mcp " (trailing space), signed in
demo: https://example.com/mcp (HTTP) - ✔ Connected
MCP config diagnostics ⚠
[Contains warnings] User config (available in all your projects)
└ [Warning] [demo] mcpServers.demo: Leading or trailing whitespace in: url
# remove the trailing space from mcpServers.demo.url in ~/.claude.json
$ claude mcp list
demo: https://example.com/mcp (HTTP) - ! Needs authentication
# mcpOAuth in .credentials.json now has two keys:
# demo|<old hash> -> the complete, still-valid token entry (orphaned)
# demo|<new hash> -> a new stub with an empty accessToken
Steps to Reproduce
claude mcp add --transport http --scope user demo "https://example.com/mcp "(any OAuth-protected HTTP MCP server, with a trailing space in the URL).- Sign in to
demovia/mcp.claude mcp listshows✔ Connectedtogether with the whitespace warning.claude doctorsuggests removing the whitespace. - Remove the trailing space from
mcpServers.demo.urlin~/.claude.json. - Run
claude mcp list:demoshows! Needs authentication. - In
.credentials.json, the old entry is still there under the old key. Copy it to the new key (demo|<sha256 of the trimmed config>) andclaude mcp listshows✔ Connectedagain, with no new sign-in.
I reproduced this on 2.1.296 and 2.1.300 against a local stub MCP server, using a separate CLAUDE_CONFIG_DIR and a hand-written token entry. With the whitespace in the URL, the stub received Authorization: Bearer <stored token>. With the whitespace removed, it received no Authorization header and Claude Code started OAuth discovery. After moving the entry to the new key, the bearer token was sent again.
Claude Model
_No response_
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.300 (Claude Code)
Platform
Anthropic API
Operating System
Other Linux
Terminal/Shell
Windows Terminal
Additional Information
- OS details: NixOS on WSL2 (Linux x64). Nothing about this is platform-specific: the key comes from the config as written, and Linux stores the credentials in a plain file.
- Code that adds the key (2.1.300 bundle): `
${name}|${sha256(JSON.stringify({type:n.type,url:n.url,headers:n.headers||{}})).substring(0,16)}. The whitespace check (value !== value.trim()`) runs when the config is loaded and only adds the warning. The config is used unchanged afterwards. - Since 2.1.281, deduplication between plugin or claude.ai connectors and configured servers treats URLs that differ only in host letter case, default port or a trailing slash as the same server. The credential key still uses the raw URL, so those spellings also land on different keys.
- Related, same key scheme, different triggers: #100817 (removing a server from one scope deletes the token another scope still uses), #93446 (client secret stored under a headers-stripped key, looked up under a headers-included one), #93331 (
mcpOAuthgrowing because entries are re-stored under changingserverUrls). #80387 asked for the whitespace warning to be documented.