One read of Claude Code CLIclaude-code-20261004T053701Z
8 pages moved out of 220 read.
Pages moved
8
significant first
Pages read
220
in this capture
Captured
05:37 UTC
Corpus hash
d247b3bd0620
corpus-hash
What this read moved
1-8 of 8claude-apps-gateway-config Changed · +31 / -4 lines
#### Start sessions on a model the policy allows
from line 780
780780 - match: {}
781781 cli:
782782 availableModels: [claude-opus-4-8, claude-sonnet-4-6, claude-haiku-4-5]
783 # Make the Default option in /model resolve inside each policy's
784 # list. The eng-contractors policy inherits enforceAvailableModels.
785 enforceAvailableModels: true
783786```
784787
785788A `match: {}` catch-all, conventionally listed last, is treated as a base layer. Every other policy inherits any key it doesn't set from the catch-all, so per-role entries only need to list what differs from the org default. The merge rules depend on the key type:
from line 791
788791* **Deny-lists and hook arrays**: `permissions.deny`, `permissions.ask`, `disabledMcpjsonServers`, `deniedMcpServers`, `blockedMarketplaces`, and every `hooks` event-type array. These take the union of base and policy, so an org-wide deny or audit hook can't be accidentally dropped by a per-role override.
789792* **Record-typed keys**: `env`, `modelOverrides`, and `skillOverrides`. These shallow-merge, so a per-role `env` block overrides keys it sets and inherits the rest from the base.
790793
791`availableModels` is also enforced server-side at `/v1/messages`, so a denied model returns `400` regardless of what the client sends.
794`availableModels` is also enforced server-side at `/v1/messages`, so a denied model returns `400` regardless of what the client sends. An empty list denies every model. The check also covers the model a session starts on before the developer picks one, so [start sessions on a model the policy allows](#start-sessions-on-a-model-the-policy-allows).
792795
793796The gateway validates the `model` value itself before it relays a request, so a malformed value never reaches an upstream. It rejects the request with a `400` in two cases:
794797
from line 818
815818 * **Group membership**: changing a user's group membership changes which policy matches them. This takes effect on the next session re-mint, meaning the next silent refresh, bounded by `session.ttl_hours`.
816819</Note>
817820
821#### Start sessions on a model the policy allows
822
823If `availableModels` leaves out Claude Code's default model, sessions get `400` responses until the developer picks a listed model, for example with `/model`. In gateway sessions the default is the Opus model the `opus` alias resolves to, and `availableModels` on its own doesn't change it.
824
825To fix this, set [`enforceAvailableModels: true`](/docs/en/model-config#enforce-the-allowlist-for-the-default-model) in the same `cli` block, then check which kind of entry the list has:
826
827* **An alias such as `sonnet`, or a built-in ID such as `claude-sonnet-4-6`**: sessions start on one of those models, and the Default option in `/model` resolves to it
828* **No alias or built-in ID in the list**: sessions can keep starting on the built-in default, so also set [`model`](/docs/en/model-config#control-the-model-users-run-on) to one of the listed IDs in that policy's `cli` block
829
830This policy lists one custom ID that [`models`](#models) defines, and starts sessions on that ID:
831
832```yaml theme={null}
833managed:
834 policies:
835 - match: { groups: [restricted-projects] }
836 cli:
837 availableModels: [claude-opus-restricted]
838 enforceAvailableModels: true
839 model: claude-opus-restricted
840```
841
818842#### Matcher values that stop the gateway at boot
819843
820844At boot, the gateway checks the `match` block of every policy and the [`admin_groups`](#admin) list. Any of these values stops the gateway with an error that names the field:
from line 872
848872 cli:
849873 # Model access (also enforced server-side at /v1/messages)
850874 availableModels: [claude-opus-4-8, claude-sonnet-4-6, claude-haiku-4-5]
875 enforceAvailableModels: true # Default resolves inside the list
851876
852877 # Permission policy
853878 permissions:
from line 988
963988 - match: { groups: [eng-contractors] }
964989 cli:
965990 availableModels: [claude-sonnet-4-6]
991 enforceAvailableModels: true
966992 desktop:
967993 isLocalDevMcpEnabled: false
968994 disableAutoUpdates: true
from line 1401
13751401 - match: { groups: [contractors] }
13761402 cli:
13771403 availableModels: [claude-haiku-4-5]
1378 # Constrain the Default picker option to availableModels instead of
1379 # the tier default, so contractors don't get a 400 on the default.
1380 enforceAvailableModels: true
13811404 # allow auto-approves these tools; it does not block the rest.
13821405 # Add deny rules to restrict tools.
13831406 permissions: { allow: [Read, Grep] }
from line 1407
13841407 - match: {}
13851408 cli:
13861409 availableModels: [claude-opus-4-8, claude-sonnet-4-6, claude-haiku-4-5]
1410 # Constrain the Default picker option to each policy's availableModels
1411 # instead of the built-in default, so no role gets a 400 on Default.
1412 # The contractors policy inherits this key.
1413 enforceAvailableModels: true
13871414 permissions:
13881415 allow: [Read, Grep, Bash, Edit]
13891416 deny: ["WebFetch"]
claude-apps-gateway-deploy Changed · +8 / -1 lines
from line 377
377377
378378The gateway answers `431` when a request's headers total more than 256 KiB, or more than [`limits.max_request_header_bytes`](/docs/en/claude-apps-gateway-config#http-tuning) if you set it. It writes no log line or audit event for these requests. Gateway versions before v2.1.284 answer `431` above 16 KiB.
379379
380What to change depends on your gateway's version and configuration:
380Start with the first of these that applies to your gateway:
381381
382382* **Gateway older than v2.1.284**: upgrade the gateway
383383* **`limits.max_request_header_bytes` set**: raise the value or remove the key
384384* **Neither applies, or `431` continues afterward**: have your IdP emit fewer groups. [Identity provider setup](#identity-provider-setup) covers how Okta, Microsoft Entra ID, and Google Workspace supply groups
385
386When you trim the groups claim, keep the groups you named in these settings, which decide a developer's access, policy, and spend caps:
387
388* **[`oidc.allowed_groups`](/docs/en/claude-apps-gateway-config#oidc)**: decides who can sign in
389* **[`admin.admin_groups`](/docs/en/claude-apps-gateway-config#admin)**: decides who can call the admin API with their gateway session
390* **`match.groups` in [`managed.policies`](/docs/en/claude-apps-gateway-config#managed)**: decides which policy applies to a developer
391* **`rbac_group` [spend caps](/docs/en/claude-apps-gateway-spend-limits)**: decide which group caps apply to a developer
385392
386393## Related
387394
managed-mcp Changed · +57 / -31 lines
#### How `serverName` entries match #### How `serverCommand` entries match #### How `serverUrl` entries match #### Servers that skip the allowlist check #### How policy entries expand
from line 244
244244
245245Allowlists and denylists filter which configured servers are allowed to load. They aren't a registry: a server still has to be added by a user, a plugin, or your organization before either list applies to it.
246246
247Servers your organization delivers through `managedMcpServers` load without an allowlist entry, and [How a server is evaluated](#how-a-server-is-evaluated) covers `managed-mcp.json` servers. The denylist applies to every server regardless of where it came from, other than in-process `type: "sdk"` entries.
247Servers your organization delivers through `managedMcpServers` load without an allowlist entry, and [Servers that skip the allowlist check](#servers-that-skip-the-allowlist-check) covers `managed-mcp.json` servers. The denylist applies to every server regardless of where it came from, other than in-process `type: "sdk"` entries.
248248
249249To deploy servers to users, use [`managed-mcp.json`](#exclusive-control-with-managed-mcp-json) or [`managedMcpServers`](#provide-servers-through-managed-settings). Both lists also filter servers a user passes with the [`--mcp-config` CLI flag](/docs/en/cli-reference#cli-flags), other than in-process `type: "sdk"` entries; `--strict-mcp-config` limits which configuration files load and doesn't bypass either list.
250250
from line 268
268268| :- | :- | :- |
269269| `serverUrl` | A remote server URL, exact or with `*` wildcards | HTTP and SSE servers |
270270| `serverCommand` | The exact command and arguments that start a stdio server | Stdio servers |
271| `serverName` | The user-assigned label. Exact match only; wildcards are not expanded | Either type, but see the Warning below |
271| `serverName` | The user-assigned label. Exact match only; wildcards are not expanded | Either type, but see [How `serverName` entries match](#how-servername-entries-match) |
272272
273273Leaving `allowedMcpServers` unset is different from setting it to an empty array:
274274
275275| Setting | Unset (default) | Empty array `[]` | Populated |
276276| :- | :- | :- | :- |
277| `allowedMcpServers` | All servers allowed | No servers allowed, apart from [those that skip the allowlist check](#how-a-server-is-evaluated) | Only matching servers allowed, apart from [those that skip the allowlist check](#how-a-server-is-evaluated) |
277| `allowedMcpServers` | All servers allowed | No servers allowed, apart from [those that skip the allowlist check](#servers-that-skip-the-allowlist-check) | Only matching servers allowed, apart from [those that skip the allowlist check](#servers-that-skip-the-allowlist-check) |
278278| `deniedMcpServers` | No servers blocked | No servers blocked | Matching servers blocked |
279279
280280See [Invalid entries in managed settings](/docs/en/managed-settings#invalid-entries-in-managed-settings) for what happens when an entry fails schema validation.
281281
282#### How `serverName` entries match
283
284A `serverName` entry matches the user-assigned label exactly, with no wildcards.
285
282286<Warning>
283287 A `serverName` entry, in either list, is not a security control. The name is the label a user assigns when running `claude mcp add` or editing a config file, not the underlying server, so a user can call any server `github`. For claude.ai connectors the name is the display name returned by claude.ai, which can change. To enforce which servers actually run, add `serverCommand` or `serverUrl` entries.
284288</Warning>
from line 294
290294
291295To turn off all the claude.ai connectors Claude Code fetches itself, see [`disableClaudeAiConnectors`](/docs/en/mcp#disable-claude-ai-connectors).
292296
293### How a server is evaluated
297#### How `serverCommand` entries match
294298
295Before loading a server, including one from `managed-mcp.json`, Claude Code runs the three checks below in order. It runs them again when a user reconnects a server or turns a disabled one back on in `/mcp`. In-process `type: "sdk"` servers, which the [app that started the session registers](/docs/en/mcp#how-connectors-reach-claude-code), skip all three.
299A `serverCommand` entry holds the command and its arguments as one array, as in `{ "serverCommand": ["npx", "-y", "server"] }`. Claude Code compares that array with the command and arguments in the server's configuration:
296300
2971. **Merge the lists.** Allowlist and denylist entries from every settings scope combine into one allowlist and one denylist. When `allowManagedMcpServersOnly` is `true`, only the managed allowlist is kept; the denylist always merges from every scope. When more than one managed source is present, [Keys read from every admin source](/docs/en/managed-settings#keys-read-from-every-admin-source) says which of them supply the managed scope's lists.
2982. **Check the denylist.** A server that matches any denylist entry, by URL, command, or name, is blocked. Nothing overrides a denylist match.
2993. **Check the allowlist.** If `allowedMcpServers` isn't set anywhere, every server that passed the denylist loads. If it is set, what the server must match depends on its type, shown in the table below.
301* **Commands match exactly.** Every argument, in order. `["npx", "-y", "server"]` does not match `["npx", "server"]` or `["npx", "-y", "server", "--flag"]`.
302* **The `env` block isn't compared.** `["node", "server.js"]` matches a server that runs that command with any `env` values. Some environment variables change what `node` loads at startup. To set the `env` values yourself, define the server in [`managed-mcp.json`](#exclusive-control-with-managed-mcp-json).
300303
301 Three groups of servers skip this check:
304#### How `serverUrl` entries match
302305
303 * The organization's own servers: every `managedMcpServers` entry, and any `managed-mcp.json` entry whose values use no `${VAR}` expansion.
304 * Built-in servers, such as Claude in Chrome, the `ide` server Claude Code connects to in a running VS Code or JetBrains IDE, and servers the CLI itself configures.
305 * A [Claude Tag](/docs/en/claude-tag) session's Slack tools: the servers it uses to read the thread and post its replies load without an allowlist entry.
306URLs support `*` wildcards anywhere in the pattern, including the scheme. Hostname matching is case-insensitive and ignores a trailing FQDN dot, so `https://Mcp.Example.com/*` matches `https://mcp.example.com/api`. Paths stay case-sensitive.
306307
307 A `managed-mcp.json` server that uses `${VAR}` expansion in its command, arguments, `env`, URL, or headers is still checked. So is every server a user, a plugin, or claude.ai adds, and every server a user passes with `--mcp-config`.
308The table shows what common patterns allow:
308309
309| Server type | Allowed when it matches |
310| :- | :- |
311| Remote (HTTP or SSE) | A `serverUrl` entry. A `serverName` match counts only when the allowlist contains no `serverUrl` entries |
312| Stdio | A `serverCommand` entry. A `serverName` match counts only when the allowlist contains no `serverCommand` entries |
313
314Three matching rules apply inside those checks:
315
316* **Commands match exactly.** Every argument, in order. `["npx", "-y", "server"]` does not match `["npx", "server"]` or `["npx", "-y", "server", "--flag"]`.
317* **`serverCommand` and `serverUrl` values expand before matching.** Both the policy entry and the server's configured value go through [`${VAR}` and `${VAR:-default}` expansion](/docs/en/mcp#environment-variable-expansion-in-mcp-json), so an entry written as `["${HOME}/bin/server"]` matches a server config that uses either the same reference or the expanded path. On Windows, reference an environment variable that is set there, such as `${USERPROFILE}` instead of `${HOME}`. `serverName` values match literally and never expand. The two sides read different environments; [How policy entries expand](#how-policy-entries-expand) covers which, and how allowlist and denylist entries differ.
318* **URLs support `*` wildcards** anywhere in the pattern, including the scheme. Hostname matching is case-insensitive and ignores a trailing FQDN dot, so `https://Mcp.Example.com/*` matches `https://mcp.example.com/api`. Paths stay case-sensitive.
319
320310| Pattern | Allows |
321311| :- | :- |
322312| `https://mcp.example.com/*` | All paths on a specific domain |
from line 315
325315| `http://localhost:*/*` | Any port on localhost |
326316| `*://mcp.example.com/*` | Any scheme to a specific domain |
327317
328#### How policy entries expand
318<h4 id="how-policy-entries-expand">
319 Environment variables in `serverCommand` and `serverUrl` entries
320</h4>
329321
330The server's configured value expands from the live process environment, like the rest of `.mcp.json`. A policy entry expands from a pinned environment instead, so a variable set by a project or user settings file can't change what an allowlist entry means. Because a policy entry still depends on the launching shell's value for any variable it references, use literal URLs and commands for entries you rely on for enforcement.
322`serverCommand` and `serverUrl` values expand before matching. Both the policy entry and the server's configured value go through [`${VAR}` and `${VAR:-default}` expansion](/docs/en/mcp#environment-variable-expansion-in-mcp-json), so an entry written as `["${HOME}/bin/server"]` matches a server config that uses either the same reference or the expanded path. `serverName` values match literally and never expand.
331323
324The two sides read different environments:
325
326* **The server's configured value**: expands from the live process environment, like the rest of `.mcp.json`
327* **A policy entry**: expands from a pinned environment, so a variable set by a project or user settings file can't change what an allowlist entry means
328
329Because a policy entry still depends on the launching shell's value for any variable it references, use literal URLs and commands for entries you rely on for enforcement.
330
331On Windows, reference an environment variable that is set there, such as `${USERPROFILE}` instead of `${HOME}`.
332
333The two lists expand differently:
334
332335| Entry list | Expands from | Expansion that would change a URL entry's scheme, host, or path scope |
333336| - | - | - |
334337| `allowedMcpServers` | The environment Claude Code started with, plus `env` values from managed settings | Claude Code ignores the entry |
335338| `deniedMcpServers` | The same, and a variable with no startup value and no `:-default` fills from settings files outside the repository, such as user or managed settings, which only ever widens what the entry matches | The entry still matches |
336339
337Requires Claude Code v2.1.219 or later.
340The pinned environment and the rules in this table require Claude Code v2.1.219 or later.
341
342### How a server is evaluated
343
344Before loading a server, including one from `managed-mcp.json`, Claude Code runs the three checks below in order. It runs them again when a user reconnects a server or turns a disabled one back on in `/mcp`. In-process `type: "sdk"` servers, which the [app that started the session registers](/docs/en/mcp#how-connectors-reach-claude-code), skip all three.
345
3461. **Merge the lists.** Allowlist and denylist entries from every settings scope combine into one allowlist and one denylist. When `allowManagedMcpServersOnly` is `true`, only the managed allowlist is kept; the denylist always merges from every scope. When more than one managed source is present, [Keys read from every admin source](/docs/en/managed-settings#keys-read-from-every-admin-source) says which of them supply the managed scope's lists.
3472. **Check the denylist.** A server that matches any denylist entry, by URL, command, or name, is blocked. Nothing overrides a denylist match.
3483. **Check the allowlist.** [Some servers skip this check](#servers-that-skip-the-allowlist-check). If `allowedMcpServers` isn't set anywhere, every server that passed the denylist loads. If it is set, what the server must match depends on its type, shown in the table below.
349
350| Server type | Allowed when it matches |
351| :- | :- |
352| Remote (HTTP or SSE) | A `serverUrl` entry. A `serverName` match counts only when the allowlist contains no `serverUrl` entries |
353| Stdio | A `serverCommand` entry. A `serverName` match counts only when the allowlist contains no `serverCommand` entries |
354
355#### Servers that skip the allowlist check
356
357Three groups of servers skip the allowlist check, in addition to the in-process `type: "sdk"` servers that skip [all three checks](#how-a-server-is-evaluated):
358
359* The organization's own servers: every `managedMcpServers` entry, and any `managed-mcp.json` entry whose values use no `${VAR}` expansion.
360* Built-in servers, such as Claude in Chrome, the `ide` server Claude Code connects to in a running VS Code or JetBrains IDE, and servers the CLI itself configures.
361* A [Claude Tag](/docs/en/claude-tag) session's Slack tools: the servers it uses to read the thread and post its replies load without an allowlist entry.
362
363A `managed-mcp.json` server that uses `${VAR}` expansion in its command, arguments, `env`, URL, or headers is still checked. Claude Code also checks every server a user, a plugin, or claude.ai adds, and every server a user passes with `--mcp-config`.
338364
339365### Example configuration
340366
monitoring-usage Changed · +39 / -8 lines
from line 334
334334
335335<span id="new-context-gates" />
336336
337**Content attributes under detailed beta tracing**
338
337339<Note>
338 Additional content-bearing attributes such as `new_context`, `system_prompt_preview`, `user_system_prompt`, `tool_input`, and `response.model_output` are emitted only when detailed beta tracing is active. They are not part of the stable span schema.
340 Additional content-bearing attributes such as `new_context`, `system_reminders`, `system_prompt_preview`, `user_system_prompt`, `tool_input`, and `response.model_output` are emitted only when detailed beta tracing is active. They are not part of the stable span schema.
341</Note>
339342
340 The gate on `new_context` depends on which span carries it, and each copy is truncated at the content limit (60 KB by default). On the `claude_code.tool` span it carries that tool call's result, whatever the tool, and requires `OTEL_LOG_TOOL_CONTENT=1`. On the `claude_code.interaction` span it carries the user prompt, and on the `claude_code.llm_request` span the new user messages and tool results of that request. Both of those require `OTEL_LOG_USER_PROMPTS=1`.
343These attributes appear on the spans below, and `Gated by` names the variable an attribute needs on top of detailed beta tracing. Values longer than the content limit (60 KB by default) are truncated.
341344
342 `user_system_prompt` additionally requires `OTEL_LOG_USER_PROMPTS=1`. It carries only the system prompt text you provide via the `systemPrompt` SDK option or `--system-prompt` and `--append-system-prompt` flags, truncated at the content limit (60 KB by default), and is emitted once per session rather than per request.
343</Note>
345| Attribute | Span | Description | Gated by |
346| - | - | - | - |
347| `new_context` | `claude_code.interaction` | The user prompt | `OTEL_LOG_USER_PROMPTS` |
348| `new_context` | `claude_code.llm_request` | The new user messages and tool results sent with the request | `OTEL_LOG_USER_PROMPTS` |
349| `system_reminders` | `claude_code.llm_request` | The text of the [system reminders](/docs/en/glossary#system-reminder) among the request's new messages | `OTEL_LOG_USER_PROMPTS` |
350| `system_prompt_preview` | `claude_code.llm_request` | First 500 characters of the complete system prompt sent with the request | `OTEL_LOG_USER_PROMPTS` |
351| `user_system_prompt` | `claude_code.llm_request` | Only the system prompt text you provide via the `systemPrompt` SDK option or `--system-prompt` and `--append-system-prompt` flags. Emitted once per session rather than per request | `OTEL_LOG_USER_PROMPTS` |
352| `response.model_output` | `claude_code.llm_request` | Text of the model's response to the request | `OTEL_LOG_USER_PROMPTS` |
353| `new_context` | `claude_code.tool` | The tool call's result, whatever the tool | `OTEL_LOG_TOOL_CONTENT` |
354| `tool_input` | `claude_code.tool` | The tool call's serialized input | `OTEL_LOG_TOOL_DETAILS` |
344355
356Under detailed beta tracing with `OTEL_LOG_USER_PROMPTS=1`, Claude Code also emits a `claude_code.system_prompt` event that carries the complete system prompt, truncated at the content limit. It arrives the first time a session sends each distinct system prompt, and again after compaction.
357
345358### Dynamic headers
346359
347360For enterprise environments that require dynamic authentication, you can configure a script to generate headers dynamically. Dynamic headers apply only to the `http/protobuf` and `http/json` protocols. With the `grpc` protocol, Claude Code uses only the static headers variables, `OTEL_EXPORTER_OTLP_HEADERS` and its per-signal variants.
from line 738
725738* `event.sequence`: per-process counter for ordering events, described under [Event correlation attributes](#event-correlation-attributes)
726739* `prompt_length`: Length of the prompt
727740* `prompt`: Prompt content. Redacted by default. Set `OTEL_LOG_USER_PROMPTS=1` to include it
741* `prompt_text`: Same value as `prompt`, redacted under the same gate. A backend that stores dotted attribute names as nested objects reads `prompt.id` as `id` inside an object named `prompt` and can lose the prompt string. Read `prompt_text` there instead. Requires Claude Code v2.1.287 or later
728742* `message.uuid`: UUID of the resulting user message, matching the persisted transcript entry. Absent on command dispatches, which can produce zero or many messages. Requires Claude Code v2.1.214 or later
729743* `command_name`: Command name when the prompt invokes one. Built-in and bundled command names such as `compact` or `debug` are emitted as-is; aliases such as `reset` emit as typed rather than the canonical name. Custom, plugin, and MCP command names collapse to `custom` or `mcp` unless `OTEL_LOG_TOOL_DETAILS=1` is set
730744* `command_source`: Origin of the command when present: `builtin`, `custom`, or `mcp`. Plugin-provided commands report as `custom`
from line 1546
15321546* OpenTelemetry export to your backend is opt-in and requires explicit configuration. For Anthropic's separate operational telemetry and how to disable it, see [Data usage](/docs/en/data-usage#telemetry-services)
15331547* Raw file contents and code snippets are not included in metrics or events. Trace spans are a separate data path: see the `OTEL_LOG_TOOL_CONTENT` bullet below
15341548* When authenticated via OAuth, `user.email` is included in telemetry attributes, sent only to the OTel endpoint you configure, never to Anthropic. If this is a concern for your organization, work with your telemetry backend to filter or redact this field
1535* User prompt content is not collected by default. Only prompt length is recorded. To include prompt content, set `OTEL_LOG_USER_PROMPTS=1`. Under detailed beta tracing this variable reaches further than prompt text: it also gates the [`new_context` span attribute](#new-context-gates), which carries tool results on the `claude_code.llm_request` span
1536* Assistant response text is not collected by default. Only response length is recorded. To include response text, set `OTEL_LOG_ASSISTANT_RESPONSES=1`. Like all OpenTelemetry data from Claude Code, the response text is sent only to the OTel endpoint you configure, never to Anthropic. When this variable is unset, `OTEL_LOG_USER_PROMPTS` is used as a fallback, so set `OTEL_LOG_ASSISTANT_RESPONSES=0` if you want prompt content without response content
1549* User prompt content is not collected by default. Only prompt length is recorded. To include prompt content, set `OTEL_LOG_USER_PROMPTS=1`. When enabled:
1550 * `user_prompt` events carry the prompt text in two attributes, `prompt` and [`prompt_text`](#user-prompt-event). If you drop or mask the event's prompt text by attribute name in your collector, name both attributes in the rule
1551
1552 This OpenTelemetry Collector `attributes` processor deletes both attributes in the pipelines that list it:
1553
1554 ```yaml theme={null}
1555 processors:
1556 attributes/drop-prompt-text:
1557 actions:
1558 - key: prompt
1559 action: delete
1560 - key: prompt_text
1561 action: delete
1562 ```
1563
1564 * With [tracing](#traces-beta) on, the `claude_code.interaction` span carries the prompt text in its `user_prompt` attribute
1565
1566 * Under detailed beta tracing, spans also carry the new user messages, tool results, and system reminders sent with each request, system prompt text, and model output. [Content attributes under detailed beta tracing](#new-context-gates) lists each attribute. The `claude_code.system_prompt` event carries the complete system prompt
1567* Assistant response text is not collected by default. Only response length is recorded. To include response text, set `OTEL_LOG_ASSISTANT_RESPONSES=1`. Like all OpenTelemetry data from Claude Code, the response text is sent only to the OTel endpoint you configure, never to Anthropic. When this variable is unset, `OTEL_LOG_USER_PROMPTS` is used as a fallback, so set `OTEL_LOG_ASSISTANT_RESPONSES=0` if you want prompt content without response content in events. Under detailed beta tracing, the `claude_code.llm_request` span still carries model output in [`response.model_output`](#new-context-gates), which follows `OTEL_LOG_USER_PROMPTS` rather than this variable
15371568* Tool input arguments and parameters are not logged by default. To include them, set `OTEL_LOG_TOOL_DETAILS=1`. For Claude Desktop's built-in servers, in sessions Claude Desktop owns, `tool_decision` and `tool_result` carry the `mcp_server_name`/`mcp_tool_name` pair, host-authored names rather than argument content, even with the flag off. The exception requires Claude Code v2.1.214 or later. This data is sent only to the OTEL endpoint you configure, never to Anthropic. Arguments may still contain sensitive values, so configure your telemetry backend to filter or redact these attributes as needed. When enabled:
15381569 * `tool_result` and `tool_decision` events include a `tool_parameters` attribute with Bash commands, MCP server and tool names, and skill names. Fields such as `full_command` are emitted untruncated
15391570 * `tool_result` events additionally include a `tool_input` attribute with file paths, URLs, search patterns, and other arguments. Individual values over 512 characters are truncated and the total is bounded to \~4 K characters
15401571 * `user_prompt` events include the verbatim `command_name` for custom, plugin, and MCP commands
15411572 * The [cost and token counters](#cost-counter) and the `api_request`, `api_error`, and `api_refusal` events carry real agent, skill, plugin, and MCP server and tool names in their attribution attributes
1542 * Trace spans include the same `tool_input` attribute and input-derived attributes such as `file_path`, with the same truncation as `tool_input`
1573 * The `claude_code.tool` span carries input-derived attributes such as `file_path`. Under detailed beta tracing it also carries a [`tool_input`](#new-context-gates) attribute
15431574* Tool content is not logged in trace spans by default. To include it, set `OTEL_LOG_TOOL_CONTENT=1`. The `claude_code.tool` span then carries a [`tool.output` span event](#tool-output-span-event) with raw file contents, Bash command output, and what MCP tools, WebFetch, and WebSearch return, truncated at the content limit (60 KB by default) per attribute. Results from MCP tools, WebFetch, and WebSearch require Claude Code v2.1.283 or later. Tool content also reaches spans through [`new_context`, whose gate differs per span](#new-context-gates). Configure your telemetry backend to filter or redact these attributes as needed
15441575* Raw Anthropic Messages API request and response bodies are not logged by default. To include them, set `OTEL_LOG_RAW_API_BODIES` in your shell, user settings, or managed settings. It's ignored in [project and local settings](/docs/en/settings-reference#variables-claude-code-ignores-in-env). The bodies contain the full conversation history, including the system prompt, every prior user and assistant turn, and tool results, so enabling this implies consent to everything the other `OTEL_LOG_*` content flags would reveal. Claude Code always redacts Claude's extended-thinking content from these bodies, regardless of other settings. The value you set determines how Claude Code delivers the bodies:
15451576 * With `=1`, Claude Code emits `api_request_body` and `api_response_body` log events for each API call. The events' `body` attribute carries the JSON-serialized payload, truncated at the content limit (60 KB by default)
self-hosted-environments-deploy Changed · +17 / -0 lines
#### GitHub API access without the GitHub CLI
from line 169
169169
170170The runner also reports the opt-in to Anthropic when it registers, printing `Registering as opted in to Anthropic-managed git (--use-anthropic-git-proxy)` at startup. Reporting the opt-in requires Claude Code v2.1.267 or later, and earlier versions accept the flag without reporting it or printing that line. Each session on an opted-in runner then uses either Anthropic-managed git or the per-session proxy URL. When a session uses the per-session proxy URL, the runner logs one `[runner:warn]` line saying so.
171171
172#### GitHub API access without the GitHub CLI
173
174If your runner image doesn't include the GitHub CLI, Claude Code can provide a built-in `gh`, so Claude can still open pull requests, comment, and read CI results. The built-in `gh` is for runners that use Anthropic-managed git. It supports one command, `gh api`, which calls GitHub's REST API. Requires Claude Code v2.1.287 or later in the runner image.
175
176This command opens a pull request in place of `gh pr create`. The built-in `gh` fills in `{owner}` and `{repo}` for the current repository:
177
178```bash theme={null}
179gh api repos/{owner}/{repo}/pulls -f title='Fix' -f head='my-branch' -f base='main'
180```
181
182* **Credentials**: the built-in `gh` sends its REST requests through Anthropic-managed git, which supplies the GitHub credential on Anthropic's side, so the image needs no GitHub token for it
183* **Which sessions get it**: Anthropic decides per session whether Anthropic-managed git serves the session's `gh`. When it does, the `[runner:session] governed git ACTIVE` line that the runner logs for the session shows `gh_path_shim=true`. When it doesn't, the session has no `gh`
184* **`jq`**: install `jq` in the image if you want `--jq` to work
185* **[`CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC`](/docs/en/env-vars)**: if the session environment sets it, Claude Code doesn't provide the built-in `gh`, and the session has no `gh`
186
187When the image includes the GitHub CLI, sessions use it.
188
172189#### Trust a private certificate authority with Anthropic-managed git
173190
174191This section applies if you set `GIT_SSL_CAINFO` or `GIT_SSL_NO_VERIFY` in the environment of a runner whose sessions use Anthropic-managed git. The handling it describes requires the runner to run Claude Code v2.1.283 or later.
claude-apps-gateway Changed · +1 / -1 lines
from line 454
454454
455455These guarantees apply to every session signed in through `/login`. The embedded sessions Claude Desktop launches get their policy as described in [Deliver policy to Claude Desktop sessions](#deliver-policy-to-claude-desktop-sessions), and the telemetry bullet says where their exports go.
456456
457* **Model access**: requests for models the policy doesn't grant return 400, and the `/model` picker is filtered to the policy's `availableModels` allowlist. Set [`enforceAvailableModels: true`](/docs/en/model-config#default-model-behavior) in the policy so the Default option resolves to a model inside `availableModels` instead of to Claude Code's built-in default; without it, Default stays selectable and is rejected at request time if that model isn't granted.
457* **Model access**: requests for models the policy doesn't grant return 400, and the `/model` picker is filtered to the policy's `availableModels` allowlist. This includes the model a session starts on before the developer picks one; see [Start sessions on a model the policy allows](/docs/en/claude-apps-gateway-config#start-sessions-on-a-model-the-policy-allows).
458458* **Telemetry destination**: in sessions signed in through `/login`, the CLI sends its OTLP/HTTP exports to the gateway rather than to a locally set `OTEL_EXPORTER_OTLP_ENDPOINT`, unless a policy [names your collector as the endpoint](/docs/en/claude-apps-gateway-config#export-directly-to-your-collector). The gateway relays the exports it receives to the destinations in [`telemetry.forward_to`](/docs/en/claude-apps-gateway-config#telemetry).
459459 * In the embedded sessions [Claude Desktop launches](#connect-claude-desktop), the CLI sends its exports to the configured `OTEL_EXPORTER_OTLP_ENDPOINT`. The CLI attaches the gateway session token to those exports only when that endpoint points at the gateway itself.
460460 * With no destination configured for a signal, the gateway accepts and discards it.
managed-settings Changed · +1 / -0 lines
from line 274
274274A developer's own settings files, `--settings` values, and project files never override a managed value; the [exceptions](/docs/en/settings#exceptions-to-managed-settings-precedence) only let a stricter lower-level value count. These cases sit outside that rule:
275275
276276* **The model for a session**: a managed `model` is a default, not a lock. `--model` and `ANTHROPIC_MODEL` still pick the model for that session, so deploy [`availableModels`](/docs/en/settings-reference#availablemodels) to restrict the choice.
277* **The auto-compact window for a session**: a managed [`autoCompactWindow`](/docs/en/settings-reference#autocompactwindow) is a default too. The `--autocompact` flag and the `CLAUDE_CODE_AUTO_COMPACT_WINDOW` variable still set the [auto-compact window](/docs/en/model-config#set-the-auto-compact-window) for that session.
277278* **Local admin rights**: a developer who is an administrator on the machine can edit the managed source itself, which is why MDM tooling can redeploy the profile or file on a schedule and why the HKLM registry and the macOS managed preferences domain exist.
278279* **The server-managed cache**: server-managed settings come from Anthropic's servers, and an edit to the local cache [lasts only until the next successful fetch](/docs/en/server-managed-settings#security-considerations).
279280* **Other tools**: managed settings bind Claude Code only. A developer who calls the API from another tool isn't under them.
sessions Changed · +1 / -0 lines
from line 264
264264| [Name the `<project>` directory yourself](#name-the-project-directory-yourself) | [`CLAUDE_CODE_PROJECT_DIR_NAME`](/docs/en/env-vars) | Environment variable |
265265| Change the 30-day retention | [`cleanupPeriodDays`](/docs/en/settings-reference#cleanupperioddays) | `settings.json` |
266266| Set an age limit for [Claude Desktop and Cowork transcripts](/docs/en/claude-directory#cleaned-up-automatically) | [`desktopSessionCleanupPeriodDays`](/docs/en/settings-reference#desktopsessioncleanupperioddays) | User settings, managed settings, or `--settings` |
267| Limit how large a `-p` or Agent SDK session's transcript file grows | [`CLAUDE_CODE_TRANSCRIPT_LOCAL_GC`](/docs/en/env-vars) | Environment variable |
267268| Suppress transcript writes in all modes | [`CLAUDE_CODE_SKIP_PROMPT_HISTORY`](/docs/en/env-vars) | Environment variable |
268269| Suppress writes for one non-interactive run | [`--no-session-persistence`](/docs/en/cli-reference) | CLI flag with `claude -p` |
269270