anthropics / anthropics/claude-code

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

Open
#90,041 0 comments 0 reactions 0 assignees View on GitHub
area:mcp bug has repro platform:windows
Dominant language
Python
Stars
145k
Forks
23.1k
PR merge metrics
PR metrics pending

Description

### Preflight Checklist

- [x] I have searched [existing issues](https://github.com/anthropics/claude-code/issues?q=is%3Aissue%20state%3Aopen%20label%3Abug) 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"
}
}
}
```

2. Run from that directory:
```
claude --debug-file log.txt -p "hi"
```

3. 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\\.claude.json.
```
Note the lookup key uses **forward slashes**.

4. Accept the trust dialog interactively for that workspace. Inspect `.claude.json`:
the persisted key is `C:\temp\mcp-trust-repro` — **backslashes**.

5. 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`:

```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 ` 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

```shell

```

### 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_

Contributor guide

No contributing guide indexed for this repository

Research direction

Start with the claude-desktop and CLI entry points involved in headersHelper, then trace the trust lookup in .claude.json against the project path persisted after accepting the trust dialog. Reproduce the forward-slash and backslash cases from the report on Windows, and keep the environment-variable and -Command observations separate. Done means a trusted workspace passes the gate using the persisted path format and the existing helper reproduction succeeds.

Written by the indexing model from the issue text.

Assessment

Tech stack
json, powershell, python
Domain
authentication, backend, security
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.