Skip to content

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

Open

Description

@ArchitektApx

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:mcparea:securitybugSomething isn't workingdocumentationImprovements or additions to documentationhas reproHas detailed reproduction stepsplatform:macosIssue specifically occurs on macOS

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions