# Claude Code v2.1.211

> Claude Code v2.1.211, released 15 Jul 2026 (2026-07-15). 14 entries read out of the shipped bundle. Unofficial, and not affiliated with Anthropic.

Web version: https://changelogs.core-directive.com/v/2.1.211

This release adds a new `RefreshMcpTools` built-in tool for re-syncing MCP server tool lists, a `--forward-subagent-text` CLI flag for streaming subagent output in print mode, and screenshot save-to-disk support for Claude in Chrome. The Claude Design permission model is significantly reworked from time-limited project grants to permanent durable write grants. Several improvements land for Windows Claude in Chrome browser resolution, macOS terminal bell behavior, and OAuth lock handling.

## New Features

### RefreshMcpTools — Re-sync MCP Server Tool Lists

**What**

A new built-in tool, `RefreshMcpTools`, lets Claude re-query tool lists from connected MCP servers and update its available tool set without restarting the session. It reports which tools were added or removed.

**Usage**

`# Refresh all connected servers RefreshMcpTools # Refresh a specific server RefreshMcpTools({ server: "myserver" })` **Details** - Triggers when a device or app comes online after a failed tool call, when expected tools are missing, or when a server's tools look stale after reconnection. - Returns per-server status: `refreshed` (tool list applied), `error` (previous list kept), or `not_connected` (server offline — this tool never dials). - Reports `toolCount`, `added`, and `removed` for each refreshed server. - Only activates when MCP clients are connected; hidden when no servers are configured. - Does not re-dial disconnected servers — it only re-reads over existing live connections. **Evidence** Complete new tool definition (search for `"RefreshMcpTools"`)

### `--forward-subagent-text` CLI Flag

**What**

New flag for `--print` mode that forwards subagent text and thinking blocks as structured `assistant`/`user` messages with `parent_tool_use_id` set, enabling callers to reconstruct the full agent conversation tree from the stream.

**Usage**

`claude --print --output-format=stream-json --forward-subagent-text "your prompt"` **Details** - Requires both `--print` and `--output-format=stream-json`. When explicitly specified on the CLI without those flags, the process exits with: `"Error: --forward-subagent-text requires --print and --output-format=stream-json."` When the option is supplied programmatically via the SDK `forwardSubagentText: true` init-config field, it silently degrades to `false` rather than erroring if the output conditions are not met. - Without the flag, only `tool_use` and `tool_result` content blocks from subagent turns are forwarded upstream in the progress event stream. With the flag enabled, all content types — including `text` and `thinking` blocks — are forwarded. - Forwarded subagent messages are emitted as `progress` events (`data.type === "agent_progress"`) carrying the full `assistant` or `user` message wrapper. The stream-json serializer converts these into top-level `assistant`/`user` message events with `parent_tool_use_id` set to the spawning agent's tool-use ID, allowing callers to reconstruct the full agent conversation tree. - When `forwardSubagentText` is active, thinking blocks in subagent turns are not suppressed: the thinking configuration object is returned unchanged rather than having its `display` property overridden to `"omitted"` — which is the default behaviour for non-interactive stream-json sessions. - Allows external tooling (CI pipelines, orchestration layers) to receive the complete content of nested subagent turns rather than only the top-level result. **Evidence** New CLI flag added to the argument parser (search for `"--forward-subagent-text"`); forwarding gate (search for `"forwardSubagentText"` near `"tool_use"` and `"tool_result"` checks); thinking-config passthrough in `Flc` function

### Screenshot Save-to-Disk for Claude in Chrome

**What**

The `computer` tool inside Claude in Chrome now supports a `save_to_disk` parameter that persists screenshots to a local temporary directory and returns the file path(s) in the tool result.

**Details**

- Pass `save_to_disk: true` on a `computer` or `browser_batch` tool call to save screenshots to disk.
- Claude includes the saved path(s) in its response so they can be attached for the user.
- If the screenshot directory cannot be created, the image is still returned inline.
- If `save_to_disk` has no effect in the current session context, Claude is instructed not to retry.
- Supports PNG, JPEG, WebP, and GIF output formats.
- Screenshots are saved as `screenshot-<timestamp>-<n>.<ext>` under a session-scoped temp directory.

**Evidence**

New `save_to_disk` handling function with text `"Screenshot saved to: "` (search for `"Screenshot saved to:"`)

## Improvements

### Claude Design: Permanent Durable Project Write Grants

**What**

The approval model for Claude Design project writes is fundamentally reworked. The old time-limited "4-hour project grant" from `finalize_plan scope:"project"` is replaced by a permanent durable grant that persists until explicitly revoked at claude.ai/design settings.

**Details**

- The first write to a project triggers a one-time interactive approval. After approval, Claude can write to any file in the project without further prompts until revoked.
- The approval approval message now reads: "Approving writes the listed files now, and lets Claude write to ANY file in the project…" rather than the previous "without another prompt for up to 4 hours".
- Grants are revocable at any time via claude.ai/design settings.
- `finalize_plan scope:"project"` is now deprecated — the client returns a deny message directing callers to use direct writes instead.
- `copy_files` without a `plan_token` is now always rejected (requires `finalize_plan` with explicit destination paths).
- Write batches that are too large to display in the approval dialog are rejected and must be split or issued via `finalize_plan`.
- Projects shared from another organization cannot receive durable grants — those require per-batch `plan_token` flows.
- Durable grants are not available in non-interactive sessions, plan mode, subagent sessions, or PermissionRequest-hook contexts.

**Evidence**

Replaced approval text (search for `"first write under a project grant"`)

### Windows Browser Resolution for Claude in Chrome via App Paths

**What**

On Windows, Claude in Chrome now uses the Windows Registry `App Paths` mechanism (via `reg.exe`) to locate installed browser executables for Edge and Vivaldi, rather than only falling back to `rundll32`.

**Details**

- Queries both `HKCU` and `HKLM` registry hives under `SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths` for `msedge.exe` and `vivaldi.exe`.
- Falls back to `rundll32 url,OpenURL` if registry resolution fails or the exe cannot be verified.
- Expands `%VARIABLE%` environment-variable tokens in registry values.
- Rejects results that point to a directory rather than an executable.
- Uses a timeout to avoid blocking on slow or stale registry entries.

**Evidence**

Windows App Paths registry key (search for `"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths"`)

### Audible Bell Behavior on macOS Terminal.app

**What**

When Claude Code sets up Option+Enter as a newline key on macOS Terminal.app, it now disables the audible bell instead of switching to a visual bell. Screen-reader users are not affected.

**Details**

- If Terminal.app is in screen-reader mode, the audible bell setting is left unchanged.
- The setup notification now reads "Option+Enter for newlines and no audible bell" (previously "visual bell").
- If no Terminal.app changes are needed (e.g., Shift+Return already works), a message confirms this without modifying preferences.

**Evidence**

Updated Terminal.app profile modification messages (search for `"Disabled the audible bell"`)

### `--callback-port` Validation Tightened

**What**

The MCP OAuth callback port flag (`--callback-port`) now validates that the port number falls within the valid TCP range `[1, 65535]`, rather than only checking it is a positive integer.

**Details**

- The error message now reads: "Error: --callback-port must be an integer in [1, 65535]"
- Port 0 is no longer accepted. The validation expression is `!Number.isInteger(m) || m <= 0 || m > 65535`, so port 0, negative values, and non-integer inputs (e.g. `1.5` or non-numeric strings that parse to `NaN`) are all rejected.
- The same validation is applied in two separate command paths: `claude mcp add` (for SSE and HTTP transports) and `claude mcp oauth identity-provider set`.

**Evidence**

Updated validation error string (search for `"--callback-port must be an integer in [1, 65535]"`)

### CLAUDE_CODE_RESUME_INTERRUPTED_TURN_MAX_AGE_MS Environment Variable

**What**

A new environment variable, `CLAUDE_CODE_RESUME_INTERRUPTED_TURN_MAX_AGE_MS`, controls how old an interrupted assistant turn can be before it is no longer automatically resumed on session restart. This prevents stale interrupted turns from being replayed after a long pause.

**Details**

- If `CLAUDE_CODE_RESUME_INTERRUPTED_TURN` is set and the interrupted turn's timestamp is older than this value, the resume is skipped.
- Defaults to 3,600,000 ms (1 hour) when the variable is present but not set to a valid finite positive number; set to `0` to disable age-gating entirely.
- The variable is passed through to child processes alongside the existing `CLAUDE_CODE_RESUME_INTERRUPTED_TURN`.

**Evidence**

New env var with fallback (search for `"CLAUDE_CODE_RESUME_INTERRUPTED_TURN_MAX_AGE_MS"`)

### OAuth Refresh Lock: Named Error, Compromise Detection, and Longer Stale Timeout

**What**

The OAuth token refresh lock now uses a named error class (`OAuthRefreshLockContendedError`), exposes an `isCompromised()` predicate and an abort signal, and raises the stale-lock timeout from 10 seconds to 60 seconds with a 5-second update heartbeat.

**Details**

- `OAuthRefreshLockContendedError` is thrown when the lock is contended after max attempts, replacing the previous untyped error.
- A new `onCompromised` callback receives a dedicated `AbortController` signal that is aborted when compromise is detected, allowing in-flight refreshes to be cancelled.
- The increased stale timeout (60 s) reduces false-positive "lock compromised" events on slow systems.

**Evidence**

New named error class (search for `"OAuthRefreshLockContendedError"`)

### Chrome Tool Error Classification

**What**

Errors from Chrome bridge tools (`javascript_tool`, `computer`, `get_page_text`) are now classified into structured named error types for telemetry, replacing untyped catch-all error handling.

**Details**

- JavaScript tool errors: `js_permission_denied`, `js_timeout`, `js_execution_error`, `js_exception`, `tab_not_found`.
- Computer (input/action) tool errors: `computer_permission_denied`, `computer_element_not_found`, `computer_action_failed`, `computer_zoom_shortcut_unsupported`, `computer_exception`.
- Page text extraction errors: `get_page_text_permission_denied`, `get_page_text_too_large`, `get_page_text_no_content`, `get_page_text_script_error`, `get_page_text_exception`.
- Shared cross-tool error: `navigation_blocked`, `permission_denied_user`, `authentication_failed`, `session_expired`, `domain_blocked`, `security_check_failed`.

**Evidence**

Error classification registry (search for `"js_permission_denied"`)

### Auto Front-Load of Tab Context for Navigate Without tabId

**What**

When the `navigate` tool is called with a URL but no `tabId`, Claude in Chrome now automatically issues a `tabs_context_mcp` call in the background to acquire a tab, then injects the tab context into the result. The caller no longer needs to call `tabs_context_mcp` first for simple URL navigation.

**Details**

- A short timeout bounds the background lookup; if it exceeds the limit, the tool returns an error instructing the caller to retry or call `tabs_context_mcp` explicitly.
- `url: "back"` and `url: "forward"` still require an explicit `tabId` (they have no meaningful default).
- The tab context JSON (from the front-loaded lookup) is appended to the successful navigate result for reference.

**Evidence**

Auto tab lookup logic (search for `"The hidden tabs_context_mcp lookup did not respond within"`)

### settings.local.json Ownership Verification Before Canonicalization

**What**

Before relocating the local settings consent store to the canonical git repository root, Claude now verifies via POSIX uid semantics that the user owns the root directory, its `.git` entry, and its `.claude` entry.

**Details**

- If any of those entries are owned by a different user, the store stays at the current working directory (the pre-canonicalization behavior) and a warning is logged.
- On platforms without uid semantics (Windows), canonicalization is skipped and logged as POSIX-only.
- Protects against shared-repository scenarios where a foreign-owned repo root could have its consent state hijacked.

**Evidence**

Ownership check log messages (search for `"localSettings: not canonicalizing the consent store to"`)

### PR Footer Optionally Shows Launch Surface

**What**

Under the `tengu_pr_footer_surface_suffix` feature flag, the "🤖 Generated with Claude Code" PR/commit footer now appends the surface through which the PR was opened (e.g., "via Claude Tag in Slack", "via GitHub Actions", "via Cowork").

**Details**

- Currently recognized surfaces: Claude Desktop, Mobile, Cowork, Claude Tag in Slack, Claude Tag in Teams, GitHub Actions.
- Falls back to the existing footer without a suffix if the surface cannot be determined.
- Controlled by the `tengu_pr_footer_surface_suffix` flag (gradual rollout).

**Evidence**

Surface-label resolver (search for `"Claude Tag in Slack"`) and PR footer function (search for `"via ${t}"`)

- Flag `tengu_pr_footer_surface_suffix`: Not enough to say (read for one account on one subscription tier against v2.1.211; this account: no value returned, anonymous baseline: no value returned, compiled default: on) These values were read against a different version of Claude Code, so treat them as the nearest reading available instead of one taken on this release.

## Bug Fixes

- OAuth callback port parsing now correctly rejects port 0 and enforces the full TCP range, fixing cases where a `0` port was silently accepted (search for `"--callback-port must be an integer in [1, 65535]"`)
- Interrupted turn auto-resume now skips turns that are too old, preventing unexpected re-execution of stale assistant turns after long restarts (search for `"CLAUDE_CODE_RESUME_INTERRUPTED_TURN_MAX_AGE_MS"`)
- Claude in Chrome `navigate` without a tabId no longer requires a manual `tabs_context_mcp` preflight call for URL navigation (search for `"tabs_context_mcp returned an error."`)
- `settings.local.json` consent store is no longer silently moved to a repo root that is owned by a different system user (search for `"localSettings: not canonicalizing the consent store"`)
