[BUG] Windows: `headersHelper` trust gate reads forward-slash project key
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?
Windows: headersHelper trust gate reads forward-slash project key, but the trust dialog writes backslash — MCP server can never connect
Summary
On Windows, the workspace-trust gate that guards headersHelper execution looks up the
project in .claude.json using a forward-slash path, while the interactive trust
dialog persists that project using a backslash path. The two never match, so for any
project-scoped MCP server that uses headersHelper:
- the helper is never executed;
- the server connects with no
Authorizationheader; - the remote returns 401;
- Claude Code falls back to OAuth and writes a sticky
needs-authcache entry.
The user sees a server that silently never connects. Accepting the trust dialog — the
remedy the debug message itself recommends — does not fix it, because the dialog writes
the key in the format the gate cannot read.
Environment
- Windows 11 Enterprise 26200
- Claude Code 2.1.246 (also reproduced on 2.1.241)
- Entrypoint: claude-desktop; also reproduced via the CLI binary directly
- Node v26.3.0
- Server type:
"type": "http"withheadersHelper
Impact
Breaks any project-scoped headersHelper MCP server on Windows. There is no error
surfaced in the UI — the server simply never appears. The natural user response is to
re-accept workspace trust, which cannot resolve it. Only a manual .claude.json edit works.
macOS/Linux appear unaffected (no backslashes in paths).
This is a regression: see version bisect below.
Minimal reproduction
- Create a throwaway project with a trivial helper:
C:\temp\mcp-trust-repro\helper.ps1
``powershell``
Write-Output '{"Authorization":"Bearer dummy"}'
C:\temp\mcp-trust-repro\.mcp.json
``json``
{
"mcpServers": {
"trustGateProbe": {
"type": "http",
"url": "https://example.invalid/mcp",
"headersHelper": "powershell.exe -NoLogo -NoProfile -NonInteractive -File C:/temp/mcp-trust-repro/helper.ps1"
}
}
}
- Run from that directory:
````
claude --debug-file log.txt -p "hi"
- Observe in
log.txt:
````
MCP server "trustGateProbe": headersHelper not run: this workspace has no persisted
trust; accept the trust dialog here once interactively, or set
projects["C:/temp/mcp-trust-repro"].hasTrustDialogAccepted in C:\Users\<user>\.claude.json.
Note the lookup key uses forward slashes.
- Accept the trust dialog interactively for that workspace. Inspect
.claude.json:
the persisted key is C:\temp\mcp-trust-repro — backslashes.
- Re-run step 2. The gate still reports "no persisted trust". The helper still does not run.
A/B confirmation that slash direction is the sole variable
Using an isolated config directory (CLAUDE_CONFIG_DIR) so nothing else differs, with a
single projects entry containing only hasTrustDialogAccepted: true:
| projects key | Result |
|---|---|
| C:/temp/mcp-trust-repro (forward) | Gate passes — Successfully retrieved 1 headers from headersHelper |
| C:\temp\mcp-trust-repro (backslash) | Gate blocked — headersHelper not run: this workspace has no persisted trust |
Same path, same value, same everything else. Only the slash direction changes.
Version bisect
| Version | Behaviour |
|---|---|
| 2.1.237 | No trust gate at all — Executing headersHelper… proceeds directly |
| 2.1.241 | Gate present, blocks on the forward-slash lookup |
| 2.1.246 | Gate present, blocks on the forward-slash lookup |
(Versions between 2.1.237 and 2.1.241 were not tested.)
The gate appears to have been introduced in that range. Workspaces trusted under an
earlier build carry a backslash-keyed record that the new gate cannot read, so this
surfaces on update.
Expected behaviour
The trust lookup should normalise path separators, or query using the same format the
trust dialog persists. A workspace the user has explicitly trusted should satisfy the gate.
Workaround
Manually add a forward-slash duplicate of the project key in .claude.json:
"projects": {
"C:\\temp\\mcp-trust-repro": { "hasTrustDialogAccepted": true, ... },
"C:/temp/mcp-trust-repro": { "hasTrustDialogAccepted": true }
}
Claude Code must be fully closed while editing — a running instance rewrites .claude.json
on exit and reverts the change.
Secondary issue (possibly worth splitting out)
${VAR} environment-variable expansion works in a server's command field but not inheadersHelper. In the same .mcp.json, "command": "${USERPROFILE}/foo/bar.exe" expands
correctly, while "headersHelper": "powershell.exe -File ${SOME_VAR}/helper.ps1" is passed
through literally and fails.
Also: -Command forms of headersHelper fail with did not return a valid value, while the
identical command line works when run directly from a shell — suggesting the headersHelper
string is tokenised in a way that breaks -Command payloads. Only -File <path> works.
Combined, these mean the only working form is a literal absolute path, which cannot be
committed to a shared .mcp.json used by developers with different workspace roots.
What Should Happen?
Paths need to be normalised when compared.
Error Messages/Logs
Steps to Reproduce
See explanation.
Claude Model
None
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
Claude Code 2.1.246 (also reproduced on 2.1.241)
Platform
Anthropic API
Operating System
Windows
Terminal/Shell
PowerShell
Additional Information
_No response_