Skip to lesson

AnthropicClaude Certified Architect – Foundations2026-102.4 Integrate MCP servers into Claude Code and agent workflows

Expand credentials without committing their values

Completed by the knowledge check

Learning objective

Use environment variable expansion for credentials rather than committing secrets into MCP configuration.

Core idea

Commit a credential reference, not the credential it names. A project-scoped .mcp.json can describe the same MCP integration for the whole team while each developer supplies an authentication value through the environment that launches Claude Code. The server definition remains reviewable; the secret value stays outside the shared file.

Expansion is a configuration mechanism, not a guarantee that authentication succeeds. An unset variable, a protected credential name, or a token rejected by the server can all leave the definition present while the connection fails. Diagnose those cases through the documented expansion rules and connection status rather than replacing the reference with a literal secret.

See 2.4.1 for where the file belongs. This lesson concerns how its values become executable configuration.

How it works

Put references in supported fields

Claude Code supports ${VAR} for an environment variable and ${VAR:-default} for a fallback when the variable is unset. Within .mcp.json, expansion applies to command, args, env, url, and headers. These fields cover executable paths, process arguments, values supplied to a stdio server, and HTTP connection configuration.

For an HTTP server that accepts a bearer token, an illustrative shared configuration is:

{
  "mcpServers": {
    "team-issues": {
      "type": "http",
      "url": "https://issues.example.com/mcp",
      "headers": {
        "Authorization": "Bearer ${ISSUES_MCP_TOKEN}"
      }
    }
  }
}

Replace the example URL with your approved endpoint. ISSUES_MCP_TOKEN is an application-chosen environment variable containing the token issued for that integration; it is not an Anthropic setting. The JSON deliberately contains the reference. Claude Code resolves it from its environment when constructing the server configuration.

For a stdio server, put a reference in the server entry’s env object, such as "API_KEY": "${ISSUES_MCP_TOKEN}", when that server expects API_KEY. The left-hand name is the server’s input contract; the right-hand name is the variable your launch environment supplies. Verify both against that server’s documentation instead of assuming every server uses bearer headers.

Distinguish defaults from credentials

A fallback can make a nonsecret path or service base URL portable. For example, ${API_BASE_URL:-https://api.example.com} selects a documented default base URL when the variable is unset. A default is a real value sent onward, not a reminder to configure the integration.

Do not place a working credential inside :-default. That would commit the credential in the same configuration you intended to share safely. An empty or illustrative fallback also does not establish valid authentication. For a credential, supply the intended variable and examine whether the server accepts it.

If a referenced variable is unset and has no default, Claude Code still loads the configuration. It reports a missing-variable warning for that server in claude mcp list and uses the unexpanded ${VAR} text as-is. This is not a fail-closed parser that refuses every incomplete definition. A literal placeholder can reach the server and trigger an authentication failure.

The remedy is to set the variable in Claude Code’s launch environment or, for a suitable nonsecret field, provide a fallback. Do not assume that a variable present in a separate terminal or unrelated process is available to the running Claude Code process.

Recognize protected credential names

In a remote server’s url and headers, Claude Code treats certain credential variables as empty. The documentation names Claude Code credentials such as ANTHROPIC_API_KEY and ANTHROPIC_AUTH_TOKEN, cloud credentials such as AWS_BEARER_TOKEN_BEDROCK, and other protected values such as HTTPS_PROXY and NPM_TOKEN.

For example, "Authorization": "Bearer ${ANTHROPIC_AUTH_TOKEN}" produces Bearer rather than exporting that token to the configured remote server. The server usually rejects it with a 401, and Claude Code reports a failed connection. A :-default fallback on a protected name is ignored, whether or not the variable is set. This differs from the missing-variable warning path.

Use a separately named variable for a credential intentionally issued to the MCP integration. A name outside the protected set, such as API_KEY, expands normally. The decision should begin with which credential the server is authorized to receive, rather than with how to bypass the protection around an unrelated Claude or cloud credential.

Verify without replacing references with values

claude mcp list, claude mcp get, and the /mcp detail view show ${VAR} references by name rather than as resolved values for local, project, and user servers. The /mcp detail behavior requires Claude Code v2.1.268 or later. Seeing the placeholder there is therefore not proof that expansion failed.

Inspect the connection status and missing-variable warning instead. When a protected reference is set, claude --debug-file /tmp/claude-debug.log can reveal a line containing never expanded toward a remote server. A connection failure and a displayed reference need different interpretations: one concerns execution, the other concerns how configuration is presented.

If the Agent SDK configures servers directly in code, use that language’s environment mechanism. Anthropic’s TypeScript examples use process.env.API_TOKEN in mcpServers; Python examples use os.environ["API_TOKEN"] in mcp_servers. A literal ${VAR} in an arbitrary programming-language string is not the .mcp.json expansion mechanism.

Key distinctions and trade-offs

Reference versus value: The reference is safe to review as configuration; the secret value must be supplied separately. A reference does not make the destination trustworthy.

Unset versus protected: The same reference resolves differently depending on the variable and the field:

Reference in .mcp.jsonWhat Claude Code usesWhat you see
${ISSUES_MCP_TOKEN}, variable setThe variable’s valueA working or rejected connection
${API_BASE_URL:-https://api.example.com}, unsetThe fallbackThe default endpoint
${ISSUES_MCP_TOKEN}, unset, no fallbackThe literal ${ISSUES_MCP_TOKEN} textA missing-variable warning
${ANTHROPIC_AUTH_TOKEN} in remote url/headersAn empty value; any fallback is ignoredNo warning; usually a 401 from the server

Only the first row delivers the integration’s credential, and only the second suits a committed fallback.

Configuration display versus connection evidence: A displayed ${VAR} can be intentional masking. Status and server responses tell you whether the connection worked.

Architecture / decision scenario

A team commits the issue-server example for a code-review workflow. Each engineer supplies ISSUES_MCP_TOKEN from their own launch environment. The CI process supplies a token intended for that same integration.

One developer forgets the variable. claude mcp list reports the missing reference, and the team repairs the environment rather than committing a token. Another substitutes ANTHROPIC_AUTH_TOKEN, believing any bearer token works. That protected reference becomes empty in the remote header; the team restores the integration-specific variable and credential.

The resulting review checks both the committed reference and a successful connection. It does not demand that /mcp display a secret as proof. If the workflow moves to direct SDK configuration, the team uses the language-specific environment expression rather than copying JSON interpolation into an unevaluated string.

Common exam traps

  • Committing a secret as the fallback in ${VAR:-default}.
  • Assuming an unset variable prevents the configuration from loading.
  • Treating a displayed reference as an unresolved runtime value.
  • Reusing Claude Code’s own authentication token for an unrelated server.
  • Expecting direct SDK strings to perform .mcp.json interpolation.

What to remember

Use environment references in the fields Claude Code expands, and supply integration-specific credentials separately. Diagnose ordinary missing variables, protected names, and server authentication as distinct cases. Keep the shared file free of credential values while verifying that the intended server actually connects.

Knowledge check

References