# Put it where the server reads it.

Claude Code, Codex, Gemini CLI and Grok Build have different environment and MCP configuration paths.

> **Current writer: project `.env` only.** Keyhole does not yet write the assistant configuration files below. Support for additional destinations is planned. These examples explain the receiving applications; they are not new Keyhole commands or request parameters.

 

First identify what needs the credential: your application, a command your assistant runs, or one MCP server. Those can be separate processes with separate settings. Saving a value in the project's `.env` does not automatically configure an assistant or its MCP servers.

 

For service-specific choices, see [Render MCP](https://jlumbroso.github.io/keyhole-docs/render-mcp.html) and [Neon MCP](https://jlumbroso.github.io/keyhole-docs/neon-mcp.html). If the service offers browser authorization, that may avoid a copied API key. The [assistant reference](https://jlumbroso.github.io/keyhole-docs/for-assistants.html) explains how to display the guide and initiate the supported handoff.

 

## Claude Code

 

Claude Code supports this shape in the project's `.claude/settings.local.json`. Both `TOKEN_NAME` and `not-a-secret` are placeholders; the latter is a harmless practice value:

 

```
{
  "env": {
    "TOKEN_NAME": "not-a-secret"
  }
}
```

 

This personal, project-local file supplies variables to Claude Code's environment. Its subprocesses, including Bash, hooks and stdio MCP servers, normally inherit them, subject to Claude's environment filtering. That is broader access than giving a credential to one server. Values under this top-level `env` are literal: `$HOME` is not shell-expanded. [Claude Code's env reference](https://code.claude.com/docs/en/settings-reference#env) describes precedence and trust exceptions.

 

Use strict JSON and preserve existing settings. When another program creates this file, verify that Git ignores it; the filename alone is not protection. Saved additions and changes can update Claude's environment, but removing a variable does not unset it in an already-running session. Restart Claude and reconnect the affected server after changing credentials. [Environment and reload behavior](https://code.claude.com/docs/en/env-vars#set-variables-in-settings-files).

 

A server's own `mcpServers.<server>.env` is different from the settings file's top-level `env`. Project server definitions live in `.mcp.json`; personal and MCP-local definitions live in `~/.claude.json`. Shared definitions can reference a privately supplied variable:

 

```
"env": {
  "TOKEN_NAME": "${TOKEN_NAME}"
}
```

 

For an HTTP MCP server, use its `headers` configuration or supported browser authorization. Once the settings `env` value has applied, a header such as `Authorization: Bearer ${TOKEN_NAME}` can reference it. An environment variable does not become a header automatically. Claude blocks certain provider-credential names in remote URLs and headers; use the intended service's variable name. [MCP environment references and credential restrictions](https://code.claude.com/docs/en/mcp#environment-variable-expansion-in-mcpjson).

 

## Codex

 

For commands Codex runs, its TOML configuration supports:

 

```
[shell_environment_policy]
set = { TOKEN_NAME = "not-a-secret" }
```

 

This can live in personal `~/.codex/config.toml` or a trusted project's `.codex/config.toml`. Project settings may be shared, so never put a real credential in a tracked copy. The environment policy can filter names; saving a setting does not establish that a running command received it. [Codex environment policy](https://learn.chatgpt.com/docs/config-file/config-advanced#environment-policy).

 

MCP settings are separate. Under `[mcp_servers.<server>]`, `env` supplies explicit stdio-process values; `env_vars` names variables to forward from Codex's environment. The shell policy's `set` table is not a substitute for either. For HTTP servers, `http_headers` holds literal headers, `env_http_headers` maps header names to environment-variable names, and `bearer_token_env_var` names the variable supplying the bearer token. A reference stores a name, not the credential itself. [Codex MCP configuration](https://learn.chatgpt.com/docs/extend/mcp?surface=cli).

 

## Gemini CLI

 

Gemini CLI does not provide Claude's top-level `env` setting. Its general environment comes from the launching process and its `.env` loader, which also recognizes `.gemini/.env`. Loading depends on folder trust, exclusions and existing process values; it is not a promise that every secret reaches every tool. [Gemini environment configuration](https://geminicli.com/docs/reference/configuration/#environment-variables-and-env-files).

 

For a particular stdio MCP server, use `mcpServers.<server>.env` in personal `~/.gemini/settings.json` or the project's `.gemini/settings.json`. This fragment belongs inside an existing server entry; `not-a-secret` is a harmless practice value:

 

```
"env": {
  "TOKEN_NAME": "not-a-secret"
}
```

 

Gemini CLI 0.65.0 sanitizes the inherited MCP environment first, then adds the server entry's explicit values. A real credential belongs only in a private configuration: before using a project file, verify that it is untracked and ignored. Preserve the server's command and other settings. [Pinned stdio environment construction](https://github.com/google-gemini/gemini-cli/blob/v0.65.0/packages/core/src/tools/mcp-client.ts#L2342-L2392).

 

A name reference such as `${TOKEN_NAME}` needs more care. At this version, settings references resolve before `.env` loads. If the value exists only in `.env`, the remaining reference is expanded at MCP startup against the redacted environment; names containing `TOKEN`, `KEY` or `SECRET` are removed, so that reference becomes empty. Listing the name in `security.environmentVariableRedaction.allowed` permits it through the name filter. Values already present before Gemini starts can instead resolve during the earlier settings pass. [Settings expansion](https://github.com/google-gemini/gemini-cli/blob/v0.65.0/packages/cli/src/config/settings.ts#L818-L824); [later environment loading](https://github.com/google-gemini/gemini-cli/blob/v0.65.0/packages/cli/src/config/settings.ts#L973-L984).

 

The allowed-name list affects the inherited environment, beyond one server. It also does not bypass filters for recognizable secret values, which run first. Treat a `.env`-plus-reference configuration as a path to verify, not a guaranteed delivery mechanism. [Pinned filter order](https://github.com/google-gemini/gemini-cli/blob/v0.65.0/packages/core/src/services/environmentSanitization.ts#L148-L187).

 

HTTP and SSE server entries use `headers` instead of stdio `env`; their references also expand against the sanitized environment. Restart Gemini after changing settings or `.env`, then check the intended server. [Pinned header expansion](https://github.com/google-gemini/gemini-cli/blob/v0.65.0/packages/core/src/tools/mcp-client.ts#L988-L1013); [Gemini MCP configuration](https://geminicli.com/docs/tools/mcp-server/).

 

## Grok Build

 

This section concerns the official [Grok Build](https://github.com/xai-org/grok-build) CLI, `grok`. Other tools with that executable name may use different files.

 

Grok Build's published source supports `[shell_environment_policy]` with a `set` map for commands it runs. Put that general policy in personal `~/.grok/config.toml` (or the configured Grok home), not the project's file. Project `.grok/config.toml` accepts only selected sections, including MCP servers. [Grok settings scopes](https://docs.x.ai/build/settings); [published shell-policy source](https://github.com/xai-org/grok-build/blob/2bdd1d6a6369de0e8c68132ea4539e9abd9e14a8/crates/codegen/xai-grok-tools/src/util/shell_env_policy.rs#L37-L53).

 

Its Claude compatibility path can also read `env` from `.claude/settings.json` and `.claude/settings.local.json`, with project values gated by trust. This supplies Bash and terminal commands, not MCP servers or MCP reference expansion. The compatibility reader stops once Grok's Claude import marker is set. `.envrc` values follow the same command-only path. [Pinned compatibility reader](https://github.com/xai-org/grok-build/blob/2bdd1d6a6369de0e8c68132ea4539e9abd9e14a8/crates/codegen/xai-grok-permission-rules/src/claude_settings.rs#L405-L434); [command environment handoff](https://github.com/xai-org/grok-build/blob/2bdd1d6a6369de0e8c68132ea4539e9abd9e14a8/crates/codegen/xai-grok-shell/src/agent/mvp_agent/agent_ops.rs#L4348-L4368).

 

For stdio MCP, use `[mcp_servers.<server>]` with `env`. For remote MCP, use `headers` or `bearer_token_env_var`. MCP strings support `${VAR}` references. These per-server settings can be project-local or personal; real credentials still need a private destination. [Grok MCP settings reference](https://docs.x.ai/build/settings/reference).

 

The pinned source also supports `bearer_token_file` for HTTP and SSE servers: an absolute or `~/` path to a private token file. Grok reads it on each request, so replacing that file can rotate the token without a restart. This is a separate file format and is not Keyhole's current `.env` writer. [Pinned token-file instructions](https://github.com/xai-org/grok-build/blob/2bdd1d6a6369de0e8c68132ea4539e9abd9e14a8/crates/codegen/xai-grok-pager/docs/user-guide/07-mcp-servers.md#L344-L354).

 

## Check the destination without disclosing the value

 

Keep real values out of commands pasted into chat, terminal arguments and shared configuration examples. A person can edit the appropriate private file locally while Keyhole's additional writers are pending. Preserve unrelated fields and verify Git protection for project files; a home-directory configuration has a different scope and may affect every project.

 

Restart or reconnect the receiving process as its documentation requires. Check a permitted operation or a presence-only result. A saved file, a loaded setting and a successful service operation are separate observations. Follow [file protections](https://jlumbroso.github.io/keyhole-docs/keeping-secrets-out-of-the-conversation.html) before introducing a real credential: making a value available to an assistant's subprocesses does not prevent those subprocesses from printing it.

 

## Reference status

 

Documentation and source checked on 24 October 2026. Source checks included [Codex 0.164.0's MCP environment](https://github.com/openai/codex/blob/rust-v0.164.0/codex-rs/rmcp-client/src/utils.rs#L16-L58) and [Gemini CLI 0.65.0's settings schema](https://github.com/google-gemini/gemini-cli/blob/v0.65.0/schemas/settings.schema.json). Grok's source reference is a periodically published snapshot, not verification of every installed binary. These configuration examples have not been exercised here in running assistants or signed-in service accounts.

## Chapter notes

**Writer status**

Keyhole currently writes .env. The harness-specific destinations here are configuration references, not supported Keyhole writers yet.

**No universal loader**

A shell setting and an MCP setting can affect different processes. Saved does not mean loaded.

Keyhole 0.1.0 · previews labeled separately. Updated 24 October 2026. [Online chapter](https://jlumbroso.github.io/keyhole-docs/assistant-settings.html). [Source reference](https://github.com/jlumbroso/keyhole/blob/main/docs/manual/content/assistant-settings.html).

Source access currently requires a repository invitation.
