[DOCS] With CLAUDE_CODE_SUBPROCESS_ENV_SCRUB=1, more MCP header variables read as empty than the docs say (e.g. API_KEY)

Status Open
Reported on v2.1.295
Maintainer reply None cached
Activity 0 comments · opened Oct 9, 2026

Documentation Type

Incorrect/outdated documentation

Documentation Location

https://code.claude.com/docs/en/mcp#credential-variables-that-read-as-empty

Section/Topic

MCP: Credential variables that read as empty. Also env vars: CLAUDE_CODE_SUBPROCESS_ENV_SCRUB

Current Documentation

A name outside this set, such as API_KEY, expands as written. To give the server one of the covered credentials, copy it into a variable with a name of your own and reference that name instead.

The CLAUDE_CODE_SUBPROCESS_ENV_SCRUB entry describes scrubbing only for subprocesses (Bash, hooks, stdio MCP servers). It doesn't mention remote MCP url/headers expansion.

What's Wrong or Missing?

When CLAUDE_CODE_SUBPROCESS_ENV_SCRUB=1 is set, variables that read as empty in a remote server's headers also include ones the scrub recognizes as credentials, by name or by value. In my tests:

  • API_KEY=plain-value, the name the docs give as an example that expands, reads as empty.
  • MY_HEADER="Bearer <43-char random token>" reads as empty, even though the name is your own. So the documented workaround of copying the credential into a variable with your own name doesn't help with the scrub on.

Without the scrub, both expand.

Repro on Claude Code 2.1.295 (macOS). Nothing needs to listen on the port, because only the debug log matters:

TOKEN=$(openssl rand -base64 32 | tr '+/' '-_' | tr -d '=')
CLAUDE_CODE_SUBPROCESS_ENV_SCRUB=1 API_KEY=plain-value MY_HEADER="Bearer $TOKEN" \
  claude -p hi --strict-mcp-config --setting-sources project,local --debug-file /tmp/cc-debug.log \
  --mcp-config '{"mcpServers":{"t":{"type":"http","url":"http://127.0.0.1:47899/mcp","headers":{"Authorization":"${MY_HEADER}","X-Key":"${API_KEY}"}}}}'
grep 'never expanded toward a remote server' /tmp/cc-debug.log

With the scrub on, the log names both variables (read as empty). A local test server then receives empty headers. Without the scrub, there is no log line and both values arrive.

This broke a real integration silently. T3 Code passes its MCP token via ${VAR} in the header (pingdotgg/t3code#17633, since pingdotgg/t3code#17408), and users with the scrub enabled got a 401 with no hint why.

Suggested Improvement

In "Credential variables that read as empty", add something like:

When CLAUDE_CODE_SUBPROCESS_ENV_SCRUB is set, variables the scrub treats as credentials, recognized by name or by value, also read as empty in a remote server's headers. This includes names outside the set above, such as API_KEY, and variables with your own name whose value looks like a credential.

Please also document the supported way to send a credential to a remote MCP server with the scrub on. Integrations that launch Claude Code through the Agent SDK need one. Also add a cross-reference in the CLAUDE_CODE_SUBPROCESS_ENV_SCRUB entry on the env vars page.

If this widening isn't intended, please treat this as a bug report instead.

Impact

Medium - Makes feature difficult to understand

Additional Context

  • Tested directly on Claude Code 2.1.295, macOS. T3 Code (Agent SDK) fails the same way on Linux with 2.1.296 and the scrub on; I didn't check the debug log there.
  • Checked against the docs and changelog as of 2026-10-10. Related: #38559 (scrub originally undocumented).

View original on GitHub ↗