Exact error
Match the message before using the fix
MCP server receives the literal string ${VAR}${MY_SECRET} is not expanded in mcpServers.<name>.envMCP server connects but authentication falls back to anonymous accessQuick answer
Start here
Test with a non-secret sentinel value first. If Claude Code CLI expands `${VAR}` but Claude Desktop passes the placeholder literally on the same machine, restarting Windows will not address the confirmed branch. Use the CLI for that MCP server, or use a server-documented secret-file option such as `<VAR>_FILE`. Do not paste a real token into troubleshooting output or leave it hardcoded in `.claude.json`.
Diagnosis
Why it happens
- In the confirmed Windows report, the Desktop app had the environment variable but skipped placeholder substitution in its MCP spawn path.
- The server started normally and received the literal placeholder, so the visible symptom appeared later as an authentication or anonymous-access failure.
- A missing process environment variable is a different branch: current Claude Code documentation says an unset required variable should fail configuration parsing rather than be passed through literally.
Safest first
Fixes, in order
Prove literal substitution failure without exposing a secret
Applies when: When an MCP server starts in Desktop but authentication or configuration is unexpectedly wrong
A harmless sentinel separates skipped `${VAR}` expansion from stale credentials, a missing variable, or a server-side authentication problem.
- Create a temporary non-secret user environment variable with a recognizable value such as `agentfix-env-test`.
- Reference that test variable in the same MCP `env` location that is failing.
- Use a test tool or redacted debug output that can show only the sentinel value; never print the real token.
- Compare the same configuration in Claude Code CLI and Claude Desktop.
Expected: CLI returns the sentinel value while Desktop returns the literal `${TEST_VAR}`, matching the confirmed bug branch.
Run the affected MCP server through Claude Code CLI
Applies when: When the same user-scope configuration expands correctly in CLI
The original reporter confirmed the CLI path expands the placeholder on the same Windows machine.
- Keep the secret in the user environment rather than copying it into JSON.
- Start Claude Code CLI from a fresh shell that inherits the variable.
- Connect to the same user-scope MCP server.
- Verify authentication with a harmless read-only MCP call.
Expected: The server receives the expanded value and authenticates without plaintext secrets in the MCP configuration.
Use a documented secret-file option
Applies when: Only when the specific MCP server documents a `<VAR>_FILE` or equivalent file-based credential setting
The reporter used the server's file convention as a Desktop workaround; it is not a universal MCP feature.
- Check the MCP server's official documentation for its exact file-based variable name.
- Store the secret in a file readable only by the intended Windows user.
- Pass the non-secret file path in the documented configuration field.
- Remove the temporary sentinel and restart Desktop.
Expected: The server reads the credential from its protected file without relying on Desktop placeholder expansion.
Verification
Prove the fix worked
- Use a read-only MCP operation that requires authentication.
- Confirm logs do not contain a literal `${VAR}` and do not contain the secret value.
- Reopen the chosen client and verify the server still authenticates.
Escalation
If it still fails
- Confirm whether the definition comes from user, project, plugin, or another higher-precedence scope.
- Check that the variable exists in the client process without printing its value.
- Do not treat hardcoding a token in `.claude.json` as the preferred repair; rotate any secret that was exposed during testing.
Scope
Environment and version notes
- The first-hand Desktop report used Claude Desktop 1.24012.1, Claude Code CLI 2.1.215, and Windows 11 Pro.
- The issue remained open on August 20, 2026. The npm registry listed Claude Code 2.1.237, but no Desktop fixed version was confirmed.
- Sources rechecked August 20, 2026.
Evidence
Sources
Source labels describe the evidence available on the checked date. A closed issue is not automatically a shipped fix.
First-hand Windows Desktop versus CLI control, literal placeholder evidence, and file-based workaround.
Official supported `${VAR}` expansion locations and scope precedence.
Related Desktop project-scope expansion report; closed as a duplicate of a different configuration path.