[BUG] Windows: `headersHelper` trust gate reads forward-slash project key

Status Open
Reported on v2.1.246
Maintainer reply None cached
Activity 0 comments · opened Aug 27, 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?

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 Authorization header;
  • the remote returns 401;
  • Claude Code falls back to OAuth and writes a sticky needs-auth cache 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" with headersHelper

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

  1. 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"
}
}
}
``

  1. Run from that directory:

``
claude --debug-file log.txt -p "hi"
``

  1. 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.

  1. Accept the trust dialog interactively for that workspace. Inspect .claude.json:

the persisted key is C:\temp\mcp-trust-reprobackslashes.

  1. 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 passesSuccessfully retrieved 1 headers from headersHelper |
| C:\temp\mcp-trust-repro (backslash) | Gate blockedheadersHelper 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 in
headersHelper. 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_

View original on GitHub ↗