<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: aicoding-guide</title>
    <description>The latest articles on DEV Community by aicoding-guide (@aicoding-guide).</description>
    <link>https://dev.to/aicoding-guide</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4119600%2F36237543-8d98-41c3-b6a3-c8202a09085f.png</url>
      <title>DEV Community: aicoding-guide</title>
      <link>https://dev.to/aicoding-guide</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/aicoding-guide"/>
    <language>en</language>
    <item>
      <title>Gemini CLI v0.62: gemini-3.8-flash and gemini-3.5-flash-lite added</title>
      <dc:creator>aicoding-guide</dc:creator>
      <pubDate>Thu, 01 Oct 2026 19:15:51 +0000</pubDate>
      <link>https://dev.to/aicoding-guide/gemini-cli-v062-gemini-38-flash-and-gemini-35-flash-lite-added-10a8</link>
      <guid>https://dev.to/aicoding-guide/gemini-cli-v062-gemini-38-flash-and-gemini-35-flash-lite-added-10a8</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://aicoding-guide.com/en/posts/gemini-cli-update-0-62/" rel="noopener noreferrer"&gt;https://aicoding-guide.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Gemini CLI v0.62.0 is mostly fixes with &lt;strong&gt;one model addition&lt;/strong&gt; in the middle of them.&lt;/p&gt;

&lt;p&gt;The thing to know: &lt;code&gt;gemini-3.8-flash&lt;/code&gt; and &lt;code&gt;gemini-3.5-flash-lite&lt;/code&gt; are now available. Alongside that, a fix retains the OAuth refresh token on refresh, and several fixes land on Windows shell execution.&lt;/p&gt;

&lt;p&gt;One caveat about the source: the v0.62.0 release notes are &lt;strong&gt;a list of PR titles with no per-change descriptions&lt;/strong&gt;. This article sticks to what the PR and the official configuration reference confirm, and says so where confirmation wasn't possible.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key point&lt;/strong&gt;&lt;br&gt;
What you will learn&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The two new model IDs and which tier each leads&lt;/li&gt;
&lt;li&gt;Setting a model with &lt;code&gt;model.name&lt;/code&gt; in &lt;code&gt;settings.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The OAuth, Windows and proxy fixes in this release&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The two new models
&lt;/h2&gt;

&lt;p&gt;PR #29443 adds both as the latest GA release in their respective tiers.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model ID&lt;/th&gt;
&lt;th&gt;Tier&lt;/th&gt;
&lt;th&gt;Position&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gemini-3.8-flash&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;flash&lt;/td&gt;
&lt;td&gt;Latest GA in the Flash tier&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gemini-3.5-flash-lite&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;flash-lite&lt;/td&gt;
&lt;td&gt;Latest GA in the Flash Lite tier&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The same PR promotes &lt;code&gt;gemini-3.5-flash&lt;/code&gt; and &lt;code&gt;gemini-3.1-flash-lite&lt;/code&gt; to base tier, with the two new models established as the "latest" alternatives. Existing model selections don't disappear — they move within the hierarchy.&lt;/p&gt;

&lt;p&gt;Both IDs are also &lt;strong&gt;confirmable in the official configuration reference as of October 1, 2026&lt;/strong&gt;: &lt;code&gt;gemini-3.8-flash&lt;/code&gt; appears among the &lt;code&gt;modelConfigs&lt;/code&gt; aliases, and &lt;code&gt;gemini-3.5-flash-lite&lt;/code&gt; among the model definitions.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Whether you can pick them depends on your auth type&lt;/strong&gt;&lt;br&gt;
Per the PR description, access is gated behind the &lt;code&gt;LATEST_FLASH_GA_LAUNCHED&lt;/code&gt; and &lt;code&gt;LATEST_FLASH_LITE_GA_LAUNCHED&lt;/code&gt; experiment flags, with immediate access for non-experiment auth types such as &lt;code&gt;USE_GEMINI&lt;/code&gt;, &lt;code&gt;USE_VERTEX_AI&lt;/code&gt; and &lt;code&gt;GATEWAY&lt;/code&gt;. &lt;strong&gt;That gating mechanism could not be confirmed in the official documentation.&lt;/strong&gt; If the models don't show up for you, a staged rollout is the likely reason.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Setting the model in settings.json
&lt;/h2&gt;

&lt;p&gt;Per the configuration reference, the model used for conversations is set with &lt;code&gt;model.name&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"model"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gemini-3.8-flash"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;model&lt;/code&gt; object documents these keys:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Key&lt;/th&gt;
&lt;th&gt;Default&lt;/th&gt;
&lt;th&gt;What the reference says&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;model.name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;unset&lt;/td&gt;
&lt;td&gt;The Gemini model to use for conversations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;model.maxSessionTurns&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Maximum user/model/tool turns to keep in a session; &lt;code&gt;-1&lt;/code&gt; means unlimited&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;model.compressionThreshold&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0.5&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The fraction of context usage at which to trigger context compression&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Settings files load in this order, each overriding the one before it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;System defaults: &lt;code&gt;/etc/gemini-cli/system-defaults.json&lt;/code&gt; on Linux, &lt;code&gt;C:\ProgramData\gemini-cli\system-defaults.json&lt;/code&gt; on Windows, &lt;code&gt;/Library/Application Support/GeminiCli/system-defaults.json&lt;/code&gt; on macOS&lt;/li&gt;
&lt;li&gt;User settings: &lt;code&gt;~/.gemini/settings.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Project settings: &lt;code&gt;.gemini/settings.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;System overrides: &lt;code&gt;/etc/gemini-cli/settings.json&lt;/code&gt;, or the OS equivalent&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For finer control, &lt;code&gt;modelConfigs&lt;/code&gt; holds &lt;code&gt;aliases&lt;/code&gt; — "named presets for model configs" that can inherit from one another with &lt;code&gt;extends&lt;/code&gt; — and &lt;code&gt;modelDefinitions&lt;/code&gt;, which carries each model's metadata: &lt;code&gt;tier&lt;/code&gt; (&lt;code&gt;flash-lite&lt;/code&gt;, &lt;code&gt;flash&lt;/code&gt;, &lt;code&gt;pro&lt;/code&gt;, &lt;code&gt;custom&lt;/code&gt;), &lt;code&gt;family&lt;/code&gt; (&lt;code&gt;gemini-3&lt;/code&gt;, &lt;code&gt;gemini-2.5&lt;/code&gt;, &lt;code&gt;gemma-4&lt;/code&gt;, &lt;code&gt;auto&lt;/code&gt;), &lt;code&gt;isPreview&lt;/code&gt;, and &lt;code&gt;features&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For the project context file, see &lt;a href="https://aicoding-guide.com/en/posts/gemini-cli-setup-gemini-md/" rel="noopener noreferrer"&gt;Setting up GEMINI.md in Gemini CLI&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Authentication and login persistence
&lt;/h2&gt;

&lt;p&gt;Two authentication fixes are in the list:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;"retain oauth refresh token on refresh and make credential deletion idempotent"&lt;/strong&gt; (#29339)&lt;/li&gt;
&lt;li&gt;"update auth error documentation link to valid anchor and add fallback" (#29377)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The first reads as closing a path where the refresh token was lost during a token refresh. It coincides with reports since v0.60.0 of sandboxed sessions not keeping login state, but &lt;strong&gt;the release notes carry no description, so whether those reports are resolved could not be confirmed&lt;/strong&gt;. If you are re-authenticating on every launch, v0.62.0 is worth trying first.&lt;/p&gt;

&lt;p&gt;On the MCP side, "format MCP tool call titles as structured signatures and segregate explanations" (#29341) changes how tool calls are presented.&lt;/p&gt;

&lt;h2&gt;
  
  
  Windows, proxy and the rest
&lt;/h2&gt;

&lt;p&gt;The remaining fixes concern the execution environment.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Area&lt;/th&gt;
&lt;th&gt;What the PR title says&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;PTY&lt;/td&gt;
&lt;td&gt;Improve file descriptor cleanup and execution lifecycle management (#29340)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ConPTY (Windows)&lt;/td&gt;
&lt;td&gt;Synchronize process exit lifecycle and harden PTY output finalization (#29379)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Terminal&lt;/td&gt;
&lt;td&gt;Improve terminal buffer memory management and format Windows diagnostic paths (#29380)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Proxy&lt;/td&gt;
&lt;td&gt;Normalize proxy-agent esbuild interop for environment proxy resolution (#29401)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;VS Code companion&lt;/td&gt;
&lt;td&gt;Preserve terminal focus when closing diff tabs (#29378)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ACP mode&lt;/td&gt;
&lt;td&gt;Emit &lt;code&gt;tool_call&lt;/code&gt; update prior to &lt;code&gt;request_permission&lt;/code&gt; (#29439)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UI&lt;/td&gt;
&lt;td&gt;Guard against negative layout dimensions in border rendering (#29347)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Logs&lt;/td&gt;
&lt;td&gt;Suppress uncaught AbortError logs during request cancellation (#29343)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If shell command execution has been unreliable on Windows, the three PTY and ConPTY fixes are the ones to look at; if you couldn't connect through a proxy, #29401 is yours. For all of them, &lt;strong&gt;the release notes give no description, so exactly which symptoms clear could not be confirmed&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The headline in v0.62.0 is &lt;code&gt;gemini-3.8-flash&lt;/code&gt; and &lt;code&gt;gemini-3.5-flash-lite&lt;/code&gt;, both confirmable in the official configuration reference&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;gemini-3.5-flash&lt;/code&gt; and &lt;code&gt;gemini-3.1-flash-lite&lt;/code&gt; move to base tier, with the new pair as the latest&lt;/li&gt;
&lt;li&gt;Set the model with &lt;code&gt;model.name&lt;/code&gt; in &lt;code&gt;settings.json&lt;/code&gt;; user settings live at &lt;code&gt;~/.gemini/settings.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;A fix retains the OAuth refresh token on refresh — worth taking if your login keeps dropping&lt;/li&gt;
&lt;li&gt;If the new models don't appear, a staged rollout is the likely cause, though that mechanism isn't in the official docs&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>gemini</category>
      <category>releasenotes</category>
      <category>models</category>
      <category>settingsjson</category>
    </item>
    <item>
      <title>Codex CLI v0.158: MCP OAuth client secrets and approval for elevated commands</title>
      <dc:creator>aicoding-guide</dc:creator>
      <pubDate>Wed, 30 Sep 2026 19:10:03 +0000</pubDate>
      <link>https://dev.to/aicoding-guide/codex-cli-v0158-mcp-oauth-client-secrets-and-approval-for-elevated-commands-52o7</link>
      <guid>https://dev.to/aicoding-guide/codex-cli-v0158-mcp-oauth-client-secrets-and-approval-for-elevated-commands-52o7</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://aicoding-guide.com/en/posts/codex-update-0-158/" rel="noopener noreferrer"&gt;https://aicoding-guide.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Codex CLI v0.158.0 lands changes that touch MCP, the sandbox and approvals — the parts you notice in daily use.&lt;/p&gt;

&lt;p&gt;Two items matter most: &lt;strong&gt;terminal input approval is now on by default for commands running with elevated permissions&lt;/strong&gt;, and &lt;strong&gt;Codex can now connect to MCP servers that require pre-registered OAuth client secrets&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This article works from the v0.158.0 release notes as the primary source, covering what affects your configuration, and then what followed in v0.159.0 and v0.159.2.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key point&lt;/strong&gt;&lt;br&gt;
What you will learn&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Approval becoming the default for elevated commands&lt;/li&gt;
&lt;li&gt;MCP OAuth client secret support, and where the configuration reference has not caught up&lt;/li&gt;
&lt;li&gt;The sandbox fixes on Windows, Linux and macOS&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Approvals: a changed default
&lt;/h2&gt;

&lt;p&gt;From the v0.158.0 New Features:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Terminal input approval is enabled by default for commands running with elevated permissions; runtime-only grants no longer cause unnecessary reviews.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you run commands with administrator or otherwise elevated permissions, expect an approval where there was none before. In the other direction, grants given only for the duration of a run should stop triggering repeat reviews.&lt;/p&gt;

&lt;p&gt;Two related fixes ship alongside it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Approved commands retain explicit filesystem denials" (v0.159.0 Bug Fixes)&lt;/li&gt;
&lt;li&gt;"Approval reviews now retry when new user input arrives, so a status question does not automatically abort a pending action" (v0.158.0 Bug Fixes)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For how approval policies and the sandbox combine, see &lt;a href="https://aicoding-guide.com/en/posts/codex-approval-sandbox/" rel="noopener noreferrer"&gt;Codex approval modes and sandbox&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  MCP: OAuth client secrets
&lt;/h2&gt;

&lt;p&gt;From the New Features:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Connect to MCP servers that require pre-registered OAuth client secrets, including through &lt;code&gt;codex mcp add --oauth-client-secret&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Servers that do not support Dynamic Client Registration make you register an OAuth app in their developer portal and take a client ID and secret back. Until now there was no way to hand Codex that secret, so those servers were out of reach.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;This flag is not in the configuration reference yet&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;--oauth-client-secret&lt;/code&gt; appears in the v0.158.0 release notes, but &lt;strong&gt;it was not in the official configuration reference as of September 30, 2026&lt;/strong&gt;. The OAuth keys that page does document for an MCP server are &lt;code&gt;oauth.client_id&lt;/code&gt;, &lt;code&gt;oauth.callback_port&lt;/code&gt; and &lt;code&gt;oauth.callback_url&lt;/code&gt;, and it says nothing about where a client secret is stored. Treat the flag name and behavior as a release-note claim.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;What the reference does document today stops at the client ID and the callback:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[mcp_servers.example]&lt;/span&gt;
&lt;span class="py"&gt;url&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"https://mcp.example.com/mcp"&lt;/span&gt;

&lt;span class="nn"&gt;[mcp_servers.example.oauth]&lt;/span&gt;
&lt;span class="py"&gt;client_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"your-client-id"&lt;/span&gt;
&lt;span class="py"&gt;callback_port&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8080&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same release also secures direct exec-server WebSocket connections with bearer tokens, including connections configured through app-server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sandbox fixes
&lt;/h2&gt;

&lt;p&gt;Three of the Bug Fixes concern the sandbox, split across platforms.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Platform&lt;/th&gt;
&lt;th&gt;What the release notes say&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Windows&lt;/td&gt;
&lt;td&gt;Sandbox failures involving ordinary Windows 10 paths, rejected stored credentials, and large permission policies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Linux&lt;/td&gt;
&lt;td&gt;Sandbox startup with nested writable roots&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Linux and macOS&lt;/td&gt;
&lt;td&gt;Git metadata protections preserved across writable roots&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;macOS&lt;/td&gt;
&lt;td&gt;Patch operations now recognize system path aliases covered by existing permissions, avoiding unnecessary approval prompts&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If you configure several writable roots, a nested arrangement failed to start on Linux before this release. Configuration is covered in &lt;a href="https://aicoding-guide.com/en/posts/codex-writable-roots/" rel="noopener noreferrer"&gt;Extending where Codex can write with writable_roots&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;v0.159.0 widens the protection further: &lt;code&gt;.aws&lt;/code&gt; directories are "protected by default under writable roots". That said, &lt;strong&gt;which directories are protected by default inside a writable root could not be confirmed on the official sandboxing page&lt;/strong&gt; — read it as a release-note claim.&lt;/p&gt;

&lt;h2&gt;
  
  
  TUI changes, and what followed in v0.159
&lt;/h2&gt;

&lt;p&gt;v0.158.0 makes copy-on-select and right-click paste configurable in the fullscreen TUI, and copied transcript selections keep their Markdown formatting. Mermaid flowcharts render quoted labels and ampersands, and unsupported diagrams explain why they fall back to source.&lt;/p&gt;

&lt;p&gt;The v0.159.0 New Features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Opt-in &lt;code&gt;instant_interrupt&lt;/code&gt;, letting new input steer Codex during model responses or long-running code-mode calls&lt;/li&gt;
&lt;li&gt;A compact welcome screen for new sessions, with consistent headers&lt;/li&gt;
&lt;li&gt;The warnings viewer dismisses reviewed warnings when closed; press &lt;code&gt;k&lt;/code&gt; to keep one&lt;/li&gt;
&lt;li&gt;You can scroll the transcript while deciding whether to implement a plan&lt;/li&gt;
&lt;li&gt;Native Mermaid rendering covers more flowchart edges, labels and node groups&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;instant_interrupt&lt;/code&gt; also &lt;strong&gt;was not in the configuration reference as of September 30, 2026&lt;/strong&gt;, so where it goes and what value it takes are unconfirmed. For the file's structure generally, see &lt;a href="https://aicoding-guide.com/en/posts/codex-config-toml/" rel="noopener noreferrer"&gt;Configuring Codex with config.toml&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;v0.159.2 is a Windows-only patch with a single fix: console windows no longer flash when Codex launches background processes and sandboxed commands.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;v0.158.0 turns on terminal input approval by default for commands running with elevated permissions&lt;/li&gt;
&lt;li&gt;Codex can now reach MCP servers that require pre-registered OAuth client secrets (&lt;code&gt;codex mcp add --oauth-client-secret&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Sandbox fixes land on Windows, Linux and macOS; Linux nested writable roots start correctly from v0.158.0&lt;/li&gt;
&lt;li&gt;v0.159.0 adds opt-in &lt;code&gt;instant_interrupt&lt;/code&gt; and TUI work; v0.159.2 is the Windows console-window fix alone&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--oauth-client-secret&lt;/code&gt; and &lt;code&gt;instant_interrupt&lt;/code&gt; were both absent from the configuration reference on September 30, 2026&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>codex</category>
      <category>codexcli</category>
      <category>releasenotes</category>
      <category>mcp</category>
    </item>
    <item>
      <title>Auto-approve MCP tools in Claude Code permissions: the mcp__ syntax and its wildcard limits</title>
      <dc:creator>aicoding-guide</dc:creator>
      <pubDate>Tue, 29 Sep 2026 19:20:36 +0000</pubDate>
      <link>https://dev.to/aicoding-guide/auto-approve-mcp-tools-in-claude-code-permissions-the-mcp-syntax-and-its-wildcard-limits-1eik</link>
      <guid>https://dev.to/aicoding-guide/auto-approve-mcp-tools-in-claude-code-permissions-the-mcp-syntax-and-its-wildcard-limits-1eik</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://aicoding-guide.com/en/posts/claude-code-mcp-allow-tools/" rel="noopener noreferrer"&gt;https://aicoding-guide.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Add an MCP server and Claude Code asks for approval every time one of its tools runs. Stopping for a read-only lookup on every call gets old fast.&lt;/p&gt;

&lt;p&gt;In short: put &lt;code&gt;mcp__&amp;lt;server&amp;gt;&lt;/code&gt; in &lt;code&gt;permissions.allow&lt;/code&gt; to auto-approve every tool from that server, or &lt;code&gt;mcp__&amp;lt;server&amp;gt;__&amp;lt;tool&amp;gt;&lt;/code&gt; for one tool. The catch is that &lt;strong&gt;an allow wildcard requires a literal &lt;code&gt;mcp__&amp;lt;server&amp;gt;__&lt;/code&gt; prefix&lt;/strong&gt; — a pattern like &lt;code&gt;mcp__*&lt;/code&gt; is skipped entirely.&lt;/p&gt;

&lt;p&gt;For registering the servers themselves, see &lt;a href="https://aicoding-guide.com/en/posts/claude-code-mcp-servers-setup/" rel="noopener noreferrer"&gt;Adding MCP servers to Claude Code&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key point&lt;/strong&gt;&lt;br&gt;
What you will learn&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The three rule shapes: whole server, wildcard, single tool&lt;/li&gt;
&lt;li&gt;Why allow wildcards need an anchor, and how deny and ask differ&lt;/li&gt;
&lt;li&gt;That &lt;code&gt;mcp__&lt;/code&gt; rules with parentheses are silently skipped from settings files&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The three rule shapes
&lt;/h2&gt;

&lt;p&gt;The docs put it this way: MCP rules use the server name as configured in Claude Code, optionally followed by the name of a tool from that server.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Rule&lt;/th&gt;
&lt;th&gt;What it matches&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mcp__puppeteer&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Any tool provided by the &lt;code&gt;puppeteer&lt;/code&gt; server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mcp__puppeteer__*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The same set, written as a wildcard&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mcp__puppeteer__puppeteer_navigate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Only the &lt;code&gt;puppeteer_navigate&lt;/code&gt; tool from that server&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In &lt;code&gt;settings.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"permissions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"allow"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"mcp__puppeteer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"mcp__github__get_issue"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"mcp__github__list_pull_requests"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The server name is whatever you passed to &lt;code&gt;claude mcp add&lt;/code&gt;, or the key in &lt;code&gt;.mcp.json&lt;/code&gt;. The separator is &lt;strong&gt;two underscores&lt;/strong&gt;, both between &lt;code&gt;mcp&lt;/code&gt; and the server and between the server and the tool.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Glossary&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Server name&lt;/strong&gt;: the name you chose in &lt;code&gt;claude mcp add &amp;lt;name&amp;gt; ...&lt;/code&gt;. Rename the server and every rule for it changes too, so when a team shares &lt;code&gt;.claude/settings.json&lt;/code&gt;, agree on the names in &lt;code&gt;.mcp.json&lt;/code&gt; first.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Allow wildcards need an anchor
&lt;/h2&gt;

&lt;p&gt;This is the part that catches people. The docs state that allow rules accept tool-name globs &lt;strong&gt;only after a literal &lt;code&gt;mcp__&amp;lt;server&amp;gt;__&lt;/code&gt; prefix&lt;/strong&gt;, and that the server segment must be glob-free so the rule names a specific server you configured.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;Result in &lt;code&gt;allow&lt;/code&gt;
&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mcp__puppeteer__*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Works. Matches every tool from &lt;code&gt;puppeteer&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mcp__github__get_*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Works. Matches that server's &lt;code&gt;get_&lt;/code&gt; tools&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mcp__*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Skipped&lt;/strong&gt; with a warning; auto-approves nothing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;*&lt;/code&gt; / &lt;code&gt;B*&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Skipped&lt;/strong&gt;, same as above&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;So there is no way to say "allow all MCP tools" in one allow rule. List each server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"permissions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"allow"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"mcp__puppeteer__*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"mcp__github__get_*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"mcp__linear__*"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Deny and ask follow different rules
&lt;/h2&gt;

&lt;p&gt;Deny and ask rules do accept globs in the tool-name position. The pattern must match the full tool name: &lt;code&gt;"*"&lt;/code&gt; matches every tool, and &lt;code&gt;"mcp__*"&lt;/code&gt; matches every MCP tool across all servers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"permissions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"deny"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"mcp__*"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rules are evaluated in order: &lt;strong&gt;deny, then ask, then allow&lt;/strong&gt;. The first match in that order determines the outcome, and rule specificity doesn't change it. A broad deny wins over a narrower allow, and an allow rule can't carve an exception out of a deny.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Goal&lt;/th&gt;
&lt;th&gt;Where and what&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Allow a server's tools wholesale&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;allow&lt;/code&gt;: &lt;code&gt;mcp__&amp;lt;server&amp;gt;&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Block one dangerous tool&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;deny&lt;/code&gt;: &lt;code&gt;mcp__&amp;lt;server&amp;gt;__&amp;lt;tool&amp;gt;&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Block all MCP tools&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;deny&lt;/code&gt;: &lt;code&gt;mcp__*&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Prompt every time&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ask&lt;/code&gt;: any of the same patterns&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A deny rule that names a bare tool removes the tool from Claude's context, so Claude never sees it. A scoped rule leaves the tool available and blocks matching calls when Claude attempts them.&lt;/p&gt;

&lt;p&gt;One thing to watch: a deny or ask rule whose tool name matches no known tool normally produces a startup warning to catch typos, but &lt;strong&gt;tool names containing &lt;code&gt;_&lt;/code&gt; or &lt;code&gt;*&lt;/code&gt; are exempt from that check&lt;/strong&gt;. Every &lt;code&gt;mcp__&lt;/code&gt; name qualifies, so a misspelled server name fails silently.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where rules live, and what gets ignored
&lt;/h2&gt;

&lt;p&gt;Write rules in &lt;code&gt;settings.json&lt;/code&gt; and inspect them with &lt;code&gt;/permissions&lt;/code&gt;. The dialog lists every rule and the &lt;code&gt;settings.json&lt;/code&gt; file it came from. You can open it while Claude is working: adding or removing a rule applies from Claude's next tool call in the same turn (v2.1.234 or later). For the files and their precedence, see &lt;a href="https://aicoding-guide.com/en/posts/claude-code-settings-json-permissions/" rel="noopener noreferrer"&gt;Configuring permissions in Claude Code's settings.json&lt;/a&gt;, and for driving the dialog, &lt;a href="https://aicoding-guide.com/en/posts/claude-code-permissions-command/" rel="noopener noreferrer"&gt;The /permissions command&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;mcp__ rules with parentheses are skipped&lt;/strong&gt;&lt;br&gt;
The &lt;code&gt;Bash(npm run build)&lt;/code&gt; style of narrowing by argument does not work for MCP tools. When Claude Code loads a settings file, it &lt;strong&gt;skips any &lt;code&gt;mcp__&lt;/code&gt; rule that has parentheses&lt;/strong&gt;. Skipped rules are listed in the invalid-settings dialog when an interactive session starts, and in &lt;code&gt;claude doctor&lt;/code&gt; output. To match a parameter on an MCP tool, pass a deny rule with &lt;code&gt;--disallowedTools&lt;/code&gt; instead.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There is a second case where an allow rule has no effect. If your organization has set a claude.ai connector tool to &lt;code&gt;ask&lt;/code&gt; and that setting reaches your session, allow rules for that tool don't apply: Claude Code prompts on every call, even in &lt;code&gt;auto&lt;/code&gt; and &lt;code&gt;bypassPermissions&lt;/code&gt; modes. In &lt;code&gt;dontAsk&lt;/code&gt; mode, which never prompts, it denies the call instead. Tools from connectors Claude Code fetches itself appear as &lt;code&gt;mcp__claude_ai_&amp;lt;server&amp;gt;__&amp;lt;tool&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;allow&lt;/code&gt; takes &lt;code&gt;mcp__&amp;lt;server&amp;gt;&lt;/code&gt; for a whole server and &lt;code&gt;mcp__&amp;lt;server&amp;gt;__&amp;lt;tool&amp;gt;&lt;/code&gt; for one tool&lt;/li&gt;
&lt;li&gt;An allow wildcard only works after &lt;code&gt;mcp__&amp;lt;server&amp;gt;__&lt;/code&gt;; &lt;code&gt;mcp__*&lt;/code&gt; and &lt;code&gt;*&lt;/code&gt; are skipped with a warning&lt;/li&gt;
&lt;li&gt;Deny and ask accept full tool-name globs, so &lt;code&gt;deny: ["mcp__*"]&lt;/code&gt; blocks every MCP tool&lt;/li&gt;
&lt;li&gt;Evaluation is deny → ask → allow, first match wins, regardless of specificity&lt;/li&gt;
&lt;li&gt;Parenthesised &lt;code&gt;mcp__&lt;/code&gt; rules are skipped from settings files; use &lt;code&gt;--disallowedTools&lt;/code&gt; to match a parameter&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>claudecode</category>
      <category>mcp</category>
      <category>permissions</category>
      <category>settingsjson</category>
    </item>
    <item>
      <title>Sign in to a remote MCP server in Claude Code: /mcp and claude mcp login</title>
      <dc:creator>aicoding-guide</dc:creator>
      <pubDate>Sun, 27 Sep 2026 19:21:56 +0000</pubDate>
      <link>https://dev.to/aicoding-guide/sign-in-to-a-remote-mcp-server-in-claude-code-mcp-and-claude-mcp-login-43i7</link>
      <guid>https://dev.to/aicoding-guide/sign-in-to-a-remote-mcp-server-in-claude-code-mcp-and-claude-mcp-login-43i7</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://aicoding-guide.com/en/posts/claude-code-mcp-http-oauth/" rel="noopener noreferrer"&gt;https://aicoding-guide.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Hosted MCP servers like Sentry, Linear and Notion are not usable the moment you register their URL. They need a browser sign-in first, and &lt;code&gt;claude mcp list&lt;/code&gt; shows them as &lt;code&gt;! Needs authentication&lt;/code&gt; until you do it.&lt;/p&gt;

&lt;p&gt;In short: add the server with &lt;code&gt;claude mcp add --transport http &amp;lt;name&amp;gt; &amp;lt;url&amp;gt;&lt;/code&gt;, then either run &lt;code&gt;/mcp&lt;/code&gt; in a session and choose &lt;code&gt;Authenticate&lt;/code&gt; on that server, or run &lt;code&gt;claude mcp login &amp;lt;name&amp;gt;&lt;/code&gt; from your shell. Tokens are stored securely and refreshed automatically.&lt;/p&gt;

&lt;p&gt;This article is part of a series. For every way to register a server, see &lt;a href="https://aicoding-guide.com/en/posts/claude-code-mcp-servers-setup/" rel="noopener noreferrer"&gt;Adding MCP servers to Claude Code&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key point&lt;/strong&gt;&lt;br&gt;
What you will learn&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The two ways to sign in: &lt;code&gt;/mcp&lt;/code&gt; and &lt;code&gt;claude mcp login&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--callback-port&lt;/code&gt; for a fixed redirect URI, and how to pass a pre-registered client ID&lt;/li&gt;
&lt;li&gt;What to do when a token expires or the browser never opens&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Add the server and sign in from /mcp
&lt;/h2&gt;

&lt;p&gt;Register the server over the HTTP transport. The docs use Sentry as the example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude mcp add &lt;span class="nt"&gt;--transport&lt;/span&gt; http sentry https://mcp.sentry.dev/mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Right after adding, &lt;code&gt;claude mcp list&lt;/code&gt; shows &lt;code&gt;! Needs authentication&lt;/code&gt;. That is expected; the sign-in clears it.&lt;/p&gt;

&lt;p&gt;Start a session, run &lt;code&gt;/mcp&lt;/code&gt;, select the server from the list, press Enter and choose &lt;code&gt;Authenticate&lt;/code&gt;. Your browser opens the service's sign-in page, where you approve the connection. Back in Claude Code the server's status changes to connected.&lt;/p&gt;

&lt;p&gt;Claude Code marks a remote server as needing authentication when it answers with &lt;code&gt;401 Unauthorized&lt;/code&gt; or &lt;code&gt;403 Forbidden&lt;/code&gt;. There is also a startup notice listing servers that need sign-in, so you do not have to open &lt;code&gt;/mcp&lt;/code&gt; to find them (v2.1.193 or later).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Glossary&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Dynamic Client Registration&lt;/strong&gt;: an OAuth mechanism where the client registers itself with the authorization server instead of you registering an app by hand. When a server supports it, passing the URL to &lt;code&gt;claude mcp add&lt;/code&gt; is all the setup the sign-in needs.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Sign in from the command line
&lt;/h2&gt;

&lt;p&gt;To sign in without opening a session, use &lt;code&gt;claude mcp login&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude mcp login sentry
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To clear stored credentials later, run &lt;code&gt;claude mcp logout &amp;lt;name&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Over SSH, or on Linux without a display server, the command detects that no local browser is available and prints the authorization URL rather than trying to open one. Open that URL on your own machine, then paste the full redirect URL from the address bar back at the prompt. The paste step needs an interactive terminal, so connect with &lt;code&gt;ssh -t&lt;/code&gt;. Pass &lt;code&gt;--no-browser&lt;/code&gt; to force the URL prompt even when a browser is detected.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude mcp login sentry &lt;span class="nt"&gt;--no-browser&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Non-interactive runs (&lt;code&gt;claude -p&lt;/code&gt;, the Agent SDK) have no &lt;code&gt;/mcp&lt;/code&gt; panel, so Claude Code cannot run the OAuth flow there. As of v2.1.196, with tool search enabled, it tells Claude that the server's tools are unavailable until you authorize it, so Claude can name the server instead of acting as though it were not configured. Do the sign-in itself from an interactive session or with &lt;code&gt;claude mcp login&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use pre-registered OAuth credentials
&lt;/h2&gt;

&lt;p&gt;If a server does not support Dynamic Client Registration, you get an error such as "Incompatible auth server: does not support dynamic client registration". Register an OAuth app through the service's developer portal and pass the credentials yourself.&lt;/p&gt;

&lt;p&gt;Servers that require a registered redirect URI expect the form &lt;code&gt;http://localhost:PORT/callback&lt;/code&gt;. Pick a port, register it, and pass the same port to &lt;code&gt;--callback-port&lt;/code&gt;: by default Claude Code picks a random free port, which will never match.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Flag / key&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--callback-port &amp;lt;port&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fixes the callback port. Usable on its own&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--client-id &amp;lt;id&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The client ID of your registered OAuth app&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--client-secret&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Prompts for the secret with masked input&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MCP_CLIENT_SECRET&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Supplies the secret through the environment, skipping the prompt&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude mcp add &lt;span class="nt"&gt;--transport&lt;/span&gt; http &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--client-id&lt;/span&gt; your-client-id &lt;span class="nt"&gt;--client-secret&lt;/span&gt; &lt;span class="nt"&gt;--callback-port&lt;/span&gt; 8080 &lt;span class="se"&gt;\&lt;/span&gt;
  my-server https://mcp.example.com/mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In JSON, the same settings live in an &lt;code&gt;oauth&lt;/code&gt; object. The secret stays out of the JSON and is passed with the separate &lt;code&gt;--client-secret&lt;/code&gt; flag.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude mcp add-json my-server &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s1"&gt;'{"type":"http","url":"https://mcp.example.com/mcp","oauth":{"clientId":"your-client-id","callbackPort":8080}}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--client-secret&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;oauth&lt;/code&gt; also takes &lt;code&gt;scopes&lt;/code&gt;, which pins the scopes requested during authorization (a single space-separated string, matching RFC 6749), and &lt;code&gt;authServerMetadataUrl&lt;/code&gt;, which overrides metadata discovery.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"slack"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://mcp.slack.com/mcp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"oauth"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"scopes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"channels:read chat:write search:read"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;oauth.scopes&lt;/code&gt; takes precedence over &lt;code&gt;authServerMetadataUrl&lt;/code&gt; and over whatever the server advertises at &lt;code&gt;/.well-known&lt;/code&gt;. If the authorization server advertises &lt;code&gt;offline_access&lt;/code&gt;, Claude Code appends it so the token can be refreshed without another browser sign-in.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The secret can only be set when you add the server&lt;/strong&gt;&lt;br&gt;
The client secret is stored in your system keychain (macOS) or a credentials file, never in your config. You can set it only at add time: when you authenticate with &lt;code&gt;claude mcp login&lt;/code&gt; or from &lt;code&gt;/mcp&lt;/code&gt;, Claude Code uses the stored secret and neither prompts for one nor reads &lt;code&gt;MCP_CLIENT_SECRET&lt;/code&gt;. To change it, run &lt;code&gt;claude mcp remove &amp;lt;name&amp;gt;&lt;/code&gt; and add the server again with &lt;code&gt;--client-secret&lt;/code&gt; and the same &lt;code&gt;--scope&lt;/code&gt;. Use &lt;code&gt;claude mcp get &amp;lt;name&amp;gt;&lt;/code&gt; to check whether credentials are configured.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Which file each scope writes to is covered in &lt;a href="https://aicoding-guide.com/en/posts/claude-code-mcp-scope/" rel="noopener noreferrer"&gt;claude mcp add scopes: local, project and user&lt;/a&gt;, and passing a static token through a header in &lt;a href="https://aicoding-guide.com/en/posts/claude-code-mcp-json-env/" rel="noopener noreferrer"&gt;Passing tokens from environment variables in .mcp.json&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  When sign-in expires or fails
&lt;/h2&gt;

&lt;p&gt;When a request to a server you already signed in to returns &lt;code&gt;401&lt;/code&gt;, Claude Code refreshes the stored token, reconnects and retries the request once. It flags the server in &lt;code&gt;/mcp&lt;/code&gt; only if that retry also fails.&lt;/p&gt;

&lt;p&gt;When the server rejects the stored refresh token, a notice pointing at &lt;code&gt;/mcp&lt;/code&gt; appears immediately. Open &lt;code&gt;/mcp&lt;/code&gt; and choose &lt;strong&gt;Re-authenticate&lt;/strong&gt; on that server.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symptom&lt;/th&gt;
&lt;th&gt;What to do&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;The browser does not open&lt;/td&gt;
&lt;td&gt;Copy the URL shown in the terminal and open it manually&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The redirect fails with a connection error after you authenticate&lt;/td&gt;
&lt;td&gt;Paste the full callback URL from the address bar into the URL prompt Claude Code shows&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A server with a configured &lt;code&gt;Authorization&lt;/code&gt; header returns 401 or 403&lt;/td&gt;
&lt;td&gt;It will not fall back to OAuth; the connection is reported as failed. Check the token, or remove the header&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A tool call fails with 403 &lt;code&gt;insufficient_scope&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Add the scope the server names to &lt;code&gt;oauth.scopes&lt;/code&gt;, then authenticate again from &lt;code&gt;/mcp&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That last row catches people out: Claude Code requests the scopes you pinned, not the scope the server asked for, so signing in again without adding it gives you a token that still lacks it.&lt;/p&gt;

&lt;p&gt;One gap worth naming: &lt;strong&gt;the on-disk location of OAuth access and refresh tokens could not be confirmed in the official documentation&lt;/strong&gt;. It says only that tokens are "stored securely and refreshed automatically", and names the keychain or a credentials file for the client secret alone.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Add with &lt;code&gt;claude mcp add --transport http &amp;lt;name&amp;gt; &amp;lt;url&amp;gt;&lt;/code&gt;, then sign in from &lt;code&gt;/mcp&lt;/code&gt; → &lt;code&gt;Authenticate&lt;/code&gt; or with &lt;code&gt;claude mcp login &amp;lt;name&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Revoke with &lt;code&gt;claude mcp logout &amp;lt;name&amp;gt;&lt;/code&gt; or "Clear authentication" in the &lt;code&gt;/mcp&lt;/code&gt; menu&lt;/li&gt;
&lt;li&gt;On a headless box, &lt;code&gt;claude mcp login --no-browser&lt;/code&gt; prints the URL and takes the redirect URL back&lt;/li&gt;
&lt;li&gt;Without Dynamic Client Registration, use &lt;code&gt;--client-id&lt;/code&gt; and &lt;code&gt;--callback-port&lt;/code&gt;, plus &lt;code&gt;--client-secret&lt;/code&gt; if the app has one&lt;/li&gt;
&lt;li&gt;A rejected refresh token means &lt;strong&gt;Re-authenticate&lt;/strong&gt; in &lt;code&gt;/mcp&lt;/code&gt;; a missing scope means editing &lt;code&gt;oauth.scopes&lt;/code&gt; first&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>claudecode</category>
      <category>mcp</category>
      <category>oauth</category>
      <category>authentication</category>
    </item>
    <item>
      <title>This week in Claude Code, Codex and Gemini CLI (week of September 27, 2026)</title>
      <dc:creator>aicoding-guide</dc:creator>
      <pubDate>Sat, 26 Sep 2026 19:13:54 +0000</pubDate>
      <link>https://dev.to/aicoding-guide/this-week-in-claude-code-codex-and-gemini-cli-week-of-september-27-2026-11oo</link>
      <guid>https://dev.to/aicoding-guide/this-week-in-claude-code-codex-and-gemini-cli-week-of-september-27-2026-11oo</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://aicoding-guide.com/en/posts/weekly-2026-09-27/" rel="noopener noreferrer"&gt;https://aicoding-guide.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The headline this week: &lt;strong&gt;Claude Opus 5.5 (&lt;code&gt;claude-opus-5-5&lt;/code&gt;) is now Claude Code's default Opus model&lt;/strong&gt; (v2.1.280). The official pricing table lists it at $4/MTok input and $20/MTok output, below Claude Opus 5's $5/$25.&lt;/p&gt;

&lt;p&gt;On the permissions side, the same release fixed writes through a symlinked path being approved against the wrong location. On the settings side, &lt;code&gt;"attribution": false&lt;/code&gt; became a valid shorthand, and auto mode's classifier moved to the server.&lt;/p&gt;

&lt;p&gt;Covered here: Claude Code 2.1.278 and 2.1.280 through 2.1.283, Codex CLI 0.156.1 and 0.157.0, and Gemini CLI 0.61.0.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key point&lt;/strong&gt;&lt;br&gt;
What you will learn&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Claude Opus 5.5's model ID and the prices you can verify in the official table&lt;/li&gt;
&lt;li&gt;The symlink write fix and what changed in auto mode&lt;/li&gt;
&lt;li&gt;Codex adding GPT-6 Sol and Luna, and Gemini CLI 0.61.0's security fixes&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Claude Code
&lt;/h2&gt;

&lt;h3&gt;
  
  
  2.1.280: Claude Opus 5.5 becomes the default Opus model
&lt;/h3&gt;

&lt;p&gt;The changelog reads: "&lt;strong&gt;Added Claude Opus 5.5 (&lt;code&gt;claude-opus-5-5&lt;/code&gt;), now the default Opus model — 1M context, $4/$20 per Mtok with $0.20/Mtok cache reads&lt;/strong&gt;". The official pricing page lists the same numbers.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&lt;/th&gt;
&lt;th&gt;Claude Opus 5.5&lt;/th&gt;
&lt;th&gt;Claude Opus 5&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Model ID&lt;/td&gt;
&lt;td&gt;&lt;code&gt;claude-opus-5-5&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;claude-opus-5&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Input&lt;/td&gt;
&lt;td&gt;$4 / MTok&lt;/td&gt;
&lt;td&gt;$5 / MTok&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Output&lt;/td&gt;
&lt;td&gt;$20 / MTok&lt;/td&gt;
&lt;td&gt;$25 / MTok&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cache reads&lt;/td&gt;
&lt;td&gt;$0.20 / MTok&lt;/td&gt;
&lt;td&gt;$0.50 / MTok&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5-minute cache writes&lt;/td&gt;
&lt;td&gt;$5 / MTok&lt;/td&gt;
&lt;td&gt;$6.25 / MTok&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The pricing page notes that cache hits on Opus 5.5 cost 0.05x the base input price, where most other models use the standard 0.1x multiplier. The longer your sessions run, the more that gap matters.&lt;/p&gt;

&lt;p&gt;Because it is now the &lt;em&gt;default&lt;/em&gt; Opus model, a setup that simply selects &lt;code&gt;opus&lt;/code&gt; in &lt;code&gt;/model&lt;/code&gt; switches to 5.5 after the update. To pin a version, name the model ID in &lt;code&gt;/model&lt;/code&gt; or set &lt;code&gt;ANTHROPIC_DEFAULT_OPUS_MODEL&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;If your organization pins which models can be used&lt;/strong&gt;&lt;br&gt;
2.1.283 added the &lt;code&gt;availableModelsMatch&lt;/code&gt; managed setting. With &lt;code&gt;"exact"&lt;/code&gt;, an &lt;code&gt;availableModels&lt;/code&gt; entry allows only the model version it names, so new releases stay blocked until you list them. A companion &lt;code&gt;deniedModels&lt;/code&gt; setting blocks specific models even when &lt;code&gt;availableModels&lt;/code&gt; allows them.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  2.1.280: writes through a symlinked path
&lt;/h3&gt;

&lt;p&gt;Three permission-related fixes landed together.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Writes through a symlinked path&lt;/strong&gt; were judged by their in-tree spelling rather than where they landed. The prompt now names the real destination, and &lt;code&gt;acceptEdits&lt;/code&gt;, allow rules and auto mode no longer approve a write that lands outside the tree.&lt;/li&gt;
&lt;li&gt;Auto mode retried an action over and over when a safety check declined to review it. The action is now denied once, noting that retrying won't help.&lt;/li&gt;
&lt;li&gt;Auto mode also denied actions over and over without pause when a safety check gave no answer. Retries now back off, and the turn stops with a message after ten in a row.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your allow rules are written around paths that look in-tree, writes could previously land somewhere you did not intend. For how those rules are written, see &lt;a href="https://aicoding-guide.com/en/posts/claude-code-settings-json-permissions/" rel="noopener noreferrer"&gt;Configuring permissions in Claude Code's settings.json&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.1.281: &lt;code&gt;"attribution": false&lt;/code&gt; as a shorthand
&lt;/h3&gt;

&lt;p&gt;You can now write &lt;code&gt;"attribution": false&lt;/code&gt; in &lt;code&gt;settings.json&lt;/code&gt; to hide all commit and PR attribution at once.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"attribution"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The changelog attaches a caveat: &lt;strong&gt;older CLI versions skip a settings file that holds it, so keep the object form in files shared across versions&lt;/strong&gt;. If your team commits &lt;code&gt;.claude/settings.json&lt;/code&gt;, stay with the object form below until you can confirm everyone is on 2.1.281 or later.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"attribution"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"commit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"pr"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"sessionUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each sub-key is explained in &lt;a href="https://aicoding-guide.com/en/posts/claude-code-attribution-trailer/" rel="noopener noreferrer"&gt;Removing Co-Authored-By from Claude Code commits&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.1.278: auto mode's classifier moved to the server
&lt;/h3&gt;

&lt;p&gt;For Claude API and Enterprise users, and on Bedrock, Vertex, Foundry and gateways, auto mode now defaults to the &lt;strong&gt;server-side classifier&lt;/strong&gt;. Per the changelog, that classifier does not charge for its own overhead, and you get a warning if the session falls back to a billed path.&lt;/p&gt;

&lt;p&gt;On Bedrock, Vertex, Foundry and gateways you can opt out with &lt;code&gt;CLAUDE_CODE_AUTO_MODE_SERVER=0&lt;/code&gt;. &lt;code&gt;/status&lt;/code&gt; gained an "Auto mode server" row showing whether this session's classifier runs on the server.&lt;/p&gt;

&lt;p&gt;For where auto mode sits among the permission modes, see &lt;a href="https://aicoding-guide.com/en/posts/claude-code-default-mode/" rel="noopener noreferrer"&gt;Changing Claude Code's default permission mode&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.1.282 and 2.1.283: prose width and prompt auditing
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;code&gt;maxProseWidth&lt;/code&gt; setting caps the width of Claude's prose in wide terminals. Tables and code blocks keep the full width.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/doctor prompt-audit&lt;/code&gt; (also &lt;code&gt;/checkup prompt-audit&lt;/code&gt;) audits your CLAUDE.md files, skills, agents and commands for prompting patterns written for older models.&lt;/li&gt;
&lt;li&gt;2.1.282 fixed every request failing with a 400 error in conversations whose history holds web search results the API cannot decrypt — for example, from a turn answered through a third-party gateway.&lt;/li&gt;
&lt;li&gt;2.1.280 added &lt;code&gt;CLAUDE_CODE_MAX_MCP_DESCRIPTION_LENGTH&lt;/code&gt;, which changes the 2,048-character cap on MCP tool descriptions and server instructions for every MCP server in the session.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Codex CLI
&lt;/h2&gt;

&lt;h3&gt;
  
  
  0.156.1 and 0.157.0: GPT-6 Sol and Luna
&lt;/h3&gt;

&lt;p&gt;0.156.1 was a hotfix that lets you "choose GPT-6 Sol or GPT-6 Luna from the model picker", with the rate-limit switch prompt now recommending GPT-6 Luna. 0.157.0 follows with "&lt;strong&gt;Added GPT-6 Sol and Luna, including Amazon Bedrock support and migration prompts for older models&lt;/strong&gt;".&lt;/p&gt;

&lt;h3&gt;
  
  
  0.157.0: changes to how you drive the CLI
&lt;/h3&gt;

&lt;p&gt;From the release notes' New Features, the items that touch day-to-day use:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Change&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Fullscreen transcripts&lt;/td&gt;
&lt;td&gt;Enabled by default; Shift-click extends a text selection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Background server&lt;/td&gt;
&lt;td&gt;Starts automatically for eligible interactive sessions, with recovery choices when server settings are incompatible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;f&lt;/code&gt; shortcut&lt;/td&gt;
&lt;td&gt;Forks a conversation open in another app, preserving drafts and queued prompts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/import&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Now available in remote sessions and local background-server sessions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Terminal rendering&lt;/td&gt;
&lt;td&gt;Unicode bullets, checkboxes, aligned equations and optimization notation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Among the bug fixes, network restrictions are now enforced across redirects and on ongoing HTTP and WebSocket traffic, with cancellation when a policy change revokes access. Configured proxies are also honored for realtime connections and standalone web search, including search redirects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gemini CLI
&lt;/h2&gt;

&lt;h3&gt;
  
  
  0.61.0: almost entirely security work
&lt;/h3&gt;

&lt;p&gt;v0.61.0's release notes list four substantive changes, all of them fixes.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Prevent indirect prompt injection via build file modifications and untrusted flags&lt;/strong&gt; (#29250)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Harden sandbox filesystem boundaries and isolate runtime state&lt;/strong&gt; (#29214)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Preserve explicit versioned Flash model IDs&lt;/strong&gt; (#29252) — a version-pinned ID was being resolved to something else.&lt;/li&gt;
&lt;li&gt;Ensure &lt;code&gt;AgentLoopContext&lt;/code&gt; properties survive an object spread (#29335)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are no new features. The release notes carry no detailed description of the fixes, so &lt;strong&gt;exactly which attack paths were closed could not be confirmed from the official release notes&lt;/strong&gt;. If you run Gemini CLI without the sandbox, this is a good moment to turn it on.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to check now
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Update Claude Code to 2.1.283 or later, which includes the 2.1.280 symlink write fix.&lt;/li&gt;
&lt;li&gt;If you select &lt;code&gt;opus&lt;/code&gt; in &lt;code&gt;/model&lt;/code&gt;, confirm that landing on Claude Opus 5.5 is what you want. To pin a version, name the model ID or set &lt;code&gt;ANTHROPIC_DEFAULT_OPUS_MODEL&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Before writing &lt;code&gt;"attribution": false&lt;/code&gt; into a shared &lt;code&gt;.claude/settings.json&lt;/code&gt;, confirm everyone is on 2.1.281 or later. Keep the object form if versions are mixed.&lt;/li&gt;
&lt;li&gt;If you use auto mode on Bedrock, Vertex, Foundry or a gateway, check the "Auto mode server" row in &lt;code&gt;/status&lt;/code&gt;. Set &lt;code&gt;CLAUDE_CODE_AUTO_MODE_SERVER=0&lt;/code&gt; to opt out of the server-side classifier.&lt;/li&gt;
&lt;li&gt;Update Gemini CLI to 0.61.0, and review your sandbox settings if it is not enabled.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Claude Opus 5.5 (&lt;code&gt;claude-opus-5-5&lt;/code&gt;) is Claude Code's default Opus model, listed at $4/MTok input and $20/MTok output&lt;/li&gt;
&lt;li&gt;2.1.280 makes symlinked writes judged by where they land, and fixes two auto mode retry loops&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;"attribution": false&lt;/code&gt; in 2.1.281 is convenient, but older CLI versions skip the whole settings file that holds it&lt;/li&gt;
&lt;li&gt;Codex 0.157.0 adds GPT-6 Sol and Luna and turns on fullscreen transcripts by default&lt;/li&gt;
&lt;li&gt;Gemini CLI 0.61.0 is prompt-injection and sandbox hardening, with no new features&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>weeklydigest</category>
      <category>updates</category>
      <category>claudecode</category>
      <category>codex</category>
    </item>
    <item>
      <title>Block git push --force in Claude Code with a deny rule</title>
      <dc:creator>aicoding-guide</dc:creator>
      <pubDate>Fri, 25 Sep 2026 19:08:06 +0000</pubDate>
      <link>https://dev.to/aicoding-guide/block-git-push-force-in-claude-code-with-a-deny-rule-48ep</link>
      <guid>https://dev.to/aicoding-guide/block-git-push-force-in-claude-code-with-a-deny-rule-48ep</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://aicoding-guide.com/en/posts/claude-code-deny-git-push-force/" rel="noopener noreferrer"&gt;https://aicoding-guide.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Let Claude Code drive Git and sooner or later a conflict resolution ends in &lt;code&gt;git push --force&lt;/code&gt;, taking someone else's commits with it. The way to stop that is a &lt;code&gt;permissions.deny&lt;/code&gt; entry in &lt;code&gt;settings.json&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Four lines in your project's &lt;code&gt;.claude/settings.json&lt;/code&gt; cover the common spellings.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key point&lt;/strong&gt;&lt;br&gt;
What you will learn&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The deny rules that block a force push&lt;/li&gt;
&lt;li&gt;How to cover &lt;code&gt;-f&lt;/code&gt;, &lt;code&gt;--force-with-lease&lt;/code&gt; and reordered options&lt;/li&gt;
&lt;li&gt;What a deny rule does not protect against&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The rules
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"permissions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"deny"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Bash(git push --force:*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Bash(git push -f:*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Bash(git push --force-with-lease:*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Bash(git push * --force:*)"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bash rules match the command text, with &lt;code&gt;*&lt;/code&gt; standing in for any text. &lt;code&gt;Bash(git push --force:*)&lt;/code&gt; therefore matches &lt;code&gt;git push --force origin main&lt;/code&gt; and anything else beginning that way.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Glossary&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Deny precedence&lt;/strong&gt;: rules are evaluated deny, then ask, then allow, and the first match decides. Rule specificity does not change that order, so an allow rule can never carve an exception out of a deny rule. Deny rules also apply in every permission mode, including &lt;code&gt;bypassPermissions&lt;/code&gt;, where allow rules have no effect at all.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Covering the spellings
&lt;/h2&gt;

&lt;p&gt;Because the match is against the command text, a different option order does not hit the same rule. These are the forms worth listing:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Command&lt;/th&gt;
&lt;th&gt;Pattern that matches it&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;git push --force origin main&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Bash(git push --force:*)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;git push -f origin main&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Bash(git push -f:*)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;git push origin main --force&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Bash(git push * --force:*)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;git push --force-with-lease&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Bash(git push --force-with-lease:*)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;--force-with-lease&lt;/code&gt; is safer than a bare &lt;code&gt;--force&lt;/code&gt;, but it still overwrites the remote. If your team allows it, move that one line into &lt;code&gt;ask&lt;/code&gt; rather than &lt;code&gt;deny&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A deny rule is a safety net, not a wall&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;cd repo &amp;amp;&amp;amp; git push --force&lt;/code&gt;, or a push buried in a shell script, does not start with the text your rule matches. For a branch that genuinely must not be rewritten, set branch protection on the remote — GitHub's "do not allow force pushes" — and treat the deny rule as what it is: a way to reduce local accidents.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Confirming it works
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Save the settings and start (or restart) Claude Code.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;/permissions&lt;/code&gt; and check that the four lines appear under deny, along with the settings file each came from.&lt;/li&gt;
&lt;li&gt;Ask Claude to run &lt;code&gt;git push --force origin test-branch&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The call should be refused, with the reason shown.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Related rules
&lt;/h2&gt;

&lt;p&gt;The same approach covers other commands that destroy work:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"permissions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"deny"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Bash(git reset --hard:*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Bash(git checkout -- .:*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Bash(git clean -f:*)"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the overall rule design — allow, ask and deny, and how the scopes interact — see &lt;a href="https://aicoding-guide.com/en/posts/claude-code-settings-json-permissions/" rel="noopener noreferrer"&gt;Claude Code permissions in settings.json&lt;/a&gt;. For a file-oriented example of the same mechanism, see &lt;a href="https://aicoding-guide.com/en/posts/claude-code-deny-read-env/" rel="noopener noreferrer"&gt;Stop Claude Code reading your .env&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Put &lt;code&gt;Bash(git push --force:*)&lt;/code&gt; and its siblings in &lt;code&gt;permissions.deny&lt;/code&gt; in &lt;code&gt;.claude/settings.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The match is on command text, so &lt;code&gt;-f&lt;/code&gt;, &lt;code&gt;--force-with-lease&lt;/code&gt; and reordered options each need a line&lt;/li&gt;
&lt;li&gt;Deny always beats allow, in every mode, and personal settings cannot undo a project deny&lt;/li&gt;
&lt;li&gt;Pair it with branch protection on the remote for anything you truly cannot lose&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>claudecode</category>
      <category>deny</category>
      <category>gitpushforce</category>
      <category>permissions</category>
    </item>
    <item>
      <title>Claude Code permission modes compared: default, acceptEdits, plan, auto, dontAsk, bypassPermissions</title>
      <dc:creator>aicoding-guide</dc:creator>
      <pubDate>Fri, 25 Sep 2026 19:08:04 +0000</pubDate>
      <link>https://dev.to/aicoding-guide/claude-code-permission-modes-compared-default-acceptedits-plan-auto-dontask-bypasspermissions-f5k</link>
      <guid>https://dev.to/aicoding-guide/claude-code-permission-modes-compared-default-acceptedits-plan-auto-dontask-bypasspermissions-f5k</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://aicoding-guide.com/en/posts/claude-code-default-mode/" rel="noopener noreferrer"&gt;https://aicoding-guide.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A permission mode sets what Claude can do in a session without asking you first. It is the baseline that applies to anything your &lt;code&gt;allow&lt;/code&gt; and &lt;code&gt;deny&lt;/code&gt; rules don't already decide.&lt;/p&gt;

&lt;p&gt;There are six modes. Two of them cannot be set from project settings at all, which is the detail that most often explains "my &lt;code&gt;defaultMode&lt;/code&gt; isn't doing anything".&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key point&lt;/strong&gt;&lt;br&gt;
What you will learn&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What each of the six modes runs without asking&lt;/li&gt;
&lt;li&gt;Which mode a session starts in, and the order that decides it&lt;/li&gt;
&lt;li&gt;The two values that silently do not apply in project settings&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The six modes
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mode&lt;/th&gt;
&lt;th&gt;What runs without asking&lt;/th&gt;
&lt;th&gt;Best for&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;default&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Reads only&lt;/td&gt;
&lt;td&gt;Reviewing every action yourself, sensitive work&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;acceptEdits&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Reads, file edits, and common filesystem commands (&lt;code&gt;mkdir&lt;/code&gt;, &lt;code&gt;touch&lt;/code&gt;, &lt;code&gt;mv&lt;/code&gt;, &lt;code&gt;cp&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Iterating on code you're reviewing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;plan&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Reads, plus classifier-approved commands when auto mode is available&lt;/td&gt;
&lt;td&gt;Exploring a codebase before changing it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;auto&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Everything, with background safety checks&lt;/td&gt;
&lt;td&gt;Long tasks, reducing prompt fatigue&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dontAsk&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Reads and pre-approved tools; anything that would prompt is denied&lt;/td&gt;
&lt;td&gt;Locked-down CI and scripts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;bypassPermissions&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Everything&lt;/td&gt;
&lt;td&gt;Isolated containers and VMs only&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The mode that reviews every action is labelled &lt;strong&gt;Manual&lt;/strong&gt; in the CLI, the VS Code and JetBrains extensions and the desktop app. Its config value is &lt;code&gt;default&lt;/code&gt;, and the CLI also accepts &lt;code&gt;manual&lt;/code&gt; as an alias (v2.1.200 or later).&lt;/p&gt;

&lt;p&gt;Modes set the baseline only. Deny rules block in every mode, including &lt;code&gt;bypassPermissions&lt;/code&gt;, and allow rules have no effect in &lt;code&gt;bypassPermissions&lt;/code&gt;. The mode decides what happens to everything your rules don't match.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which mode a session starts in
&lt;/h2&gt;

&lt;p&gt;On Pro, Max and Team plans the built-in starting permission mode is &lt;strong&gt;auto&lt;/strong&gt;. For a new terminal session, Claude Code takes the mode from the first of these that applies:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The &lt;code&gt;--permission-mode&lt;/code&gt; flag, or &lt;code&gt;--dangerously-skip-permissions&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;permissions.defaultMode&lt;/code&gt; in a settings file&lt;/li&gt;
&lt;li&gt;The built-in default&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Two values silently don't apply in project settings&lt;/strong&gt;&lt;br&gt;
Set &lt;code&gt;"auto"&lt;/code&gt; in &lt;code&gt;.claude/settings.json&lt;/code&gt; or &lt;code&gt;.claude/settings.local.json&lt;/code&gt; and it does not take effect — and Claude Code then uses the built-in default rather than a &lt;code&gt;defaultMode&lt;/code&gt; from &lt;code&gt;~/.claude/settings.json&lt;/code&gt;. Set &lt;code&gt;"bypassPermissions"&lt;/code&gt; in those two files and it doesn't take effect either: the session starts in Manual mode. Every other value applies from any settings file.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Writing the setting
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"permissions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"defaultMode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"acceptEdits"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Put it in &lt;code&gt;~/.claude/settings.json&lt;/code&gt; for a personal default across projects, &lt;code&gt;.claude/settings.json&lt;/code&gt; to share with the project, or &lt;code&gt;.claude/settings.local.json&lt;/code&gt; for yourself in this repository — keeping the exception above in mind.&lt;/p&gt;

&lt;h2&gt;
  
  
  Picking one
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Everyday development&lt;/strong&gt;: &lt;code&gt;acceptEdits&lt;/code&gt;. Edits stop interrupting you, and you review them together with &lt;code&gt;git diff&lt;/code&gt; afterwards&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An unfamiliar codebase, or design work&lt;/strong&gt;: &lt;code&gt;plan&lt;/code&gt;. Nothing is changed, so you can hand over investigation safely, then switch once you agree with the plan&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shared project settings&lt;/strong&gt;: stay on the cautious side. Anyone who wants to move faster can override in their own &lt;code&gt;settings.local.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CI and disposable containers&lt;/strong&gt;: &lt;code&gt;dontAsk&lt;/code&gt; for a strict allowlist, or &lt;code&gt;bypassPermissions&lt;/code&gt; only where Claude Code cannot damage anything&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For what &lt;code&gt;bypassPermissions&lt;/code&gt; actually turns off and the conditions the documentation sets for it, see &lt;a href="https://aicoding-guide.com/en/posts/claude-code-bypass-permissions/" rel="noopener noreferrer"&gt;When bypassPermissions is safe to use&lt;/a&gt;. For non-interactive runs, see &lt;a href="https://aicoding-guide.com/en/posts/claude-code-headless-ci/" rel="noopener noreferrer"&gt;Running Claude Code headless in CI&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Switching temporarily
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;How&lt;/th&gt;
&lt;th&gt;Scope&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Shift+Tab&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The current session; cycles through the available modes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;claude --permission-mode plan&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The starting mode for that session&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;claude -p ... --permission-mode acceptEdits&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;One non-interactive run&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;None of these rewrite a settings file. "Normally acceptEdits, today just investigating" is a &lt;code&gt;Shift+Tab&lt;/code&gt; or a flag, not a config change.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Six modes: &lt;code&gt;default&lt;/code&gt; (Manual), &lt;code&gt;acceptEdits&lt;/code&gt;, &lt;code&gt;plan&lt;/code&gt;, &lt;code&gt;auto&lt;/code&gt;, &lt;code&gt;dontAsk&lt;/code&gt;, &lt;code&gt;bypassPermissions&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Deny rules apply in every mode; allow rules do nothing in &lt;code&gt;bypassPermissions&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;On Pro, Max and Team plans, sessions start in auto unless a flag or &lt;code&gt;defaultMode&lt;/code&gt; says otherwise&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;auto&lt;/code&gt; and &lt;code&gt;bypassPermissions&lt;/code&gt; do not take effect from &lt;code&gt;.claude/settings.json&lt;/code&gt; or &lt;code&gt;.claude/settings.local.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Shift+Tab&lt;/code&gt; and &lt;code&gt;--permission-mode&lt;/code&gt; switch temporarily without touching your settings&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>claudecode</category>
      <category>defaultmode</category>
      <category>acceptedits</category>
      <category>permissions</category>
    </item>
    <item>
      <title>claude mcp add --scope: local vs project vs user, and which to pick</title>
      <dc:creator>aicoding-guide</dc:creator>
      <pubDate>Thu, 24 Sep 2026 19:17:24 +0000</pubDate>
      <link>https://dev.to/aicoding-guide/claude-mcp-add-scope-local-vs-project-vs-user-and-which-to-pick-4ajm</link>
      <guid>https://dev.to/aicoding-guide/claude-mcp-add-scope-local-vs-project-vs-user-and-which-to-pick-4ajm</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://aicoding-guide.com/en/posts/claude-code-mcp-scope/" rel="noopener noreferrer"&gt;https://aicoding-guide.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When you add an MCP server with &lt;code&gt;claude mcp add&lt;/code&gt;, it is not obvious which &lt;code&gt;--scope&lt;/code&gt; to pass. The names suggest the answer, but &lt;code&gt;local&lt;/code&gt; and &lt;code&gt;user&lt;/code&gt; are stored in the same file, which makes the distinction easy to miss.&lt;/p&gt;

&lt;p&gt;Three things separate them: &lt;strong&gt;where they are stored, who they are shared with, and which one wins a name clash&lt;/strong&gt;. In short: &lt;code&gt;local&lt;/code&gt; (the default) for yourself in this project, &lt;code&gt;project&lt;/code&gt; to share with your team, &lt;code&gt;user&lt;/code&gt; for yourself everywhere.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key point&lt;/strong&gt;&lt;br&gt;
What you will learn&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How the three scopes differ in storage and reach&lt;/li&gt;
&lt;li&gt;Why &lt;code&gt;project&lt;/code&gt; writes &lt;code&gt;.mcp.json&lt;/code&gt; and triggers an approval prompt&lt;/li&gt;
&lt;li&gt;Which scope wins when the same name appears in several&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The three scopes side by side
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scope&lt;/th&gt;
&lt;th&gt;Reach&lt;/th&gt;
&lt;th&gt;Stored in&lt;/th&gt;
&lt;th&gt;Shared with the team&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;local&lt;/code&gt; (default)&lt;/td&gt;
&lt;td&gt;This project, you only&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;~/.claude.json&lt;/code&gt;, under that project's path&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;project&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Everyone on the project&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;.mcp.json&lt;/code&gt; at the project root&lt;/td&gt;
&lt;td&gt;Yes, through version control&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;user&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;All your projects&lt;/td&gt;
&lt;td&gt;&lt;code&gt;~/.claude.json&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Both &lt;code&gt;local&lt;/code&gt; and &lt;code&gt;user&lt;/code&gt; land in &lt;code&gt;~/.claude.json&lt;/code&gt;. The difference is where inside it: the documentation says Claude Code stores a local server in &lt;code&gt;~/.claude.json&lt;/code&gt; &lt;strong&gt;under that project's path&lt;/strong&gt;, so the same server won't appear in your other projects.&lt;/p&gt;

&lt;p&gt;Omitting &lt;code&gt;--scope&lt;/code&gt; gives you &lt;code&gt;local&lt;/code&gt;. These two are equivalent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude mcp add &lt;span class="nt"&gt;--transport&lt;/span&gt; http stripe https://mcp.stripe.com
claude mcp add &lt;span class="nt"&gt;--transport&lt;/span&gt; http stripe &lt;span class="nt"&gt;--scope&lt;/span&gt; &lt;span class="nb"&gt;local &lt;/span&gt;https://mcp.stripe.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Which to pick
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Situation&lt;/th&gt;
&lt;th&gt;Scope&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A server with credentials you don't want in version control&lt;/td&gt;
&lt;td&gt;&lt;code&gt;local&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;An experimental configuration&lt;/td&gt;
&lt;td&gt;&lt;code&gt;local&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Everyone on the team should get the same server&lt;/td&gt;
&lt;td&gt;&lt;code&gt;project&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A server you use in every project (search, notes)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;user&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The documentation describes local's use cases the same way: personal development servers, experimental configurations, and servers with credentials you don't want in version control.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Shared with the team (written to .mcp.json)&lt;/span&gt;
claude mcp add &lt;span class="nt"&gt;--transport&lt;/span&gt; http shared-server &lt;span class="nt"&gt;--scope&lt;/span&gt; project https://example.com/mcp

&lt;span class="c"&gt;# Available in all your projects&lt;/span&gt;
claude mcp add &lt;span class="nt"&gt;--transport&lt;/span&gt; http hubspot &lt;span class="nt"&gt;--scope&lt;/span&gt; user https://mcp.hubspot.com/anthropic
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Project scope and .mcp.json
&lt;/h2&gt;

&lt;p&gt;Adding with &lt;code&gt;--scope project&lt;/code&gt; creates or updates &lt;code&gt;.mcp.json&lt;/code&gt; at the project root.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"shared-server"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://example.com/mcp"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Commit that file to share the configuration. For how to add MCP servers in general, see &lt;a href="https://aicoding-guide.com/en/posts/claude-code-mcp-servers-setup/" rel="noopener noreferrer"&gt;Adding MCP servers to Claude Code&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Project scope comes with an approval prompt&lt;/strong&gt;&lt;br&gt;
The documentation states that for security reasons Claude Code prompts for approval in interactive sessions before using project-scoped servers from &lt;code&gt;.mcp.json&lt;/code&gt; files. To reset those approval choices, run &lt;code&gt;claude mcp reset-project-choices&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The cases where the prompt does &lt;strong&gt;not&lt;/strong&gt; appear are documented too:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In &lt;code&gt;claude -p&lt;/code&gt; runs, Agent SDK sessions and cloud sessions, Claude Code can't show the prompt, so it loads project-scoped servers without asking&lt;/li&gt;
&lt;li&gt;It also skips the prompt in a session started in &lt;code&gt;bypassPermissions&lt;/code&gt; mode with &lt;code&gt;skipDangerousModePermissionPrompt&lt;/code&gt; set in user or managed settings&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To keep a server out anyway, the documentation gives three options:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Option&lt;/th&gt;
&lt;th&gt;Effect&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Add it to &lt;code&gt;disabledMcpjsonServers&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Blocks it in every permission mode&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Exclude project settings with &lt;code&gt;--setting-sources&lt;/code&gt; (SDK: &lt;code&gt;settingSources&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Project settings are not read at all&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Start with &lt;code&gt;--strict-mcp-config&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Only the servers you pass with &lt;code&gt;--mcp-config&lt;/code&gt; are used&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  When the same name appears twice
&lt;/h2&gt;

&lt;p&gt;When a server is defined in more than one place, Claude Code connects once, using the definition from the highest-precedence source. &lt;strong&gt;The whole entry from that source is used; fields are not merged across scopes.&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Local scope&lt;/li&gt;
&lt;li&gt;Project scope&lt;/li&gt;
&lt;li&gt;User scope&lt;/li&gt;
&lt;li&gt;Plugin-provided servers&lt;/li&gt;
&lt;li&gt;claude.ai connectors&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The three scopes match duplicates by name. Define the same name in two scopes with different endpoints and Claude Code warns about the conflict in &lt;code&gt;claude mcp list&lt;/code&gt; output and in &lt;code&gt;/mcp&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Glossary&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Scope&lt;/strong&gt;: how far a piece of configuration reaches. For MCP it is set by two questions: just you or everyone, and this project or all of them.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Listing and removing
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude mcp list
claude mcp get &amp;lt;name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Removing takes a scope:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude mcp remove &amp;lt;name&amp;gt; &lt;span class="nt"&gt;--scope&lt;/span&gt; &amp;lt;scope&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;--scope&lt;/code&gt; defaults to &lt;code&gt;local&lt;/code&gt;: yours, in this project only&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;local&lt;/code&gt; and &lt;code&gt;user&lt;/code&gt; share &lt;code&gt;~/.claude.json&lt;/code&gt;, but local is stored under the project's path&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;project&lt;/code&gt; writes &lt;code&gt;.mcp.json&lt;/code&gt; at the project root and travels through version control&lt;/li&gt;
&lt;li&gt;Project-scoped servers need approval in interactive sessions; reset with &lt;code&gt;claude mcp reset-project-choices&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;On a name clash the order is local, project, user, plugin, connector — and only one definition is used&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>claudecode</category>
      <category>mcp</category>
      <category>scope</category>
      <category>mcpjson</category>
    </item>
    <item>
      <title>Block rm -rf with a Claude Code PreToolUse hook, and know its limits</title>
      <dc:creator>aicoding-guide</dc:creator>
      <pubDate>Tue, 22 Sep 2026 19:20:30 +0000</pubDate>
      <link>https://dev.to/aicoding-guide/block-rm-rf-with-a-claude-code-pretooluse-hook-and-know-its-limits-2992</link>
      <guid>https://dev.to/aicoding-guide/block-rm-rf-with-a-claude-code-pretooluse-hook-and-know-its-limits-2992</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://aicoding-guide.com/en/posts/claude-code-hooks-block-rm/" rel="noopener noreferrer"&gt;https://aicoding-guide.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Some commands you never want running unreviewed, and &lt;code&gt;rm -rf&lt;/code&gt; is the classic one. A &lt;code&gt;PreToolUse&lt;/code&gt; hook lets a script inspect the call and refuse it before the tool runs.&lt;/p&gt;

&lt;p&gt;What makes this hook worth using is its position in the chain: the documentation states that &lt;code&gt;PreToolUse&lt;/code&gt; hooks fire &lt;strong&gt;before any permission-mode check&lt;/strong&gt;, in every permission mode including &lt;code&gt;dontAsk&lt;/code&gt;, and that a hook returning &lt;code&gt;permissionDecision: "deny"&lt;/code&gt; blocks the tool even in &lt;code&gt;bypassPermissions&lt;/code&gt; mode or with &lt;code&gt;--dangerously-skip-permissions&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key point&lt;/strong&gt;&lt;br&gt;
What you will learn&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Two ways to block: exit code 2 and &lt;code&gt;permissionDecision&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Why changing permission mode doesn't get around it, and what hooks still can't do&lt;/li&gt;
&lt;li&gt;Where string matching breaks down, and what to pair it with&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Blocking with exit code 2
&lt;/h2&gt;

&lt;p&gt;The shortest form. Write a message to &lt;code&gt;stderr&lt;/code&gt; and exit 2: the tool call is denied and that &lt;code&gt;stderr&lt;/code&gt; is fed back to Claude.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="c"&gt;# .claude/hooks/block-rm-rf.sh&lt;/span&gt;
&lt;span class="nv"&gt;INPUT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;CMD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$INPUT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.tool_input.command // empty'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CMD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-Eq&lt;/span&gt; &lt;span class="s1"&gt;'(^|[;&amp;amp;|[:space:]])rm[[:space:]]+(-[a-zA-Z]*r[a-zA-Z]*f|-[a-zA-Z]*f[a-zA-Z]*r)'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Blocked: recursive force-remove is not allowed here. Name the paths individually."&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
  &lt;span class="nb"&gt;exit &lt;/span&gt;2
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On macOS and Linux the script has to be executable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x .claude/hooks/block-rm-rf.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Register it in &lt;code&gt;.claude/settings.json&lt;/code&gt;. As in the documentation's example, you can run a logging hook alongside the guardrail:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"PreToolUse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"matcher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bash"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"jq -r .tool_input.command &amp;gt;&amp;gt; ~/.claude/bash.log"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$CLAUDE_PROJECT_DIR&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;/.claude/hooks/block-rm-rf.sh"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The documentation walks through what happens: both hooks execute &lt;strong&gt;in parallel&lt;/strong&gt;, the logging hook exits 0 and reports no decision, and the guardrail exits 2 to deny the call. &lt;strong&gt;The deny takes precedence&lt;/strong&gt;, so the command is blocked — and the log entry is still written, because the logging hook already ran.&lt;/p&gt;

&lt;h2&gt;
  
  
  Blocking with permissionDecision
&lt;/h2&gt;

&lt;p&gt;Instead of an exit code, print JSON to &lt;code&gt;stdout&lt;/code&gt;. This lets you pass a structured reason.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hookSpecificOutput"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"hookEventName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"PreToolUse"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"permissionDecision"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"deny"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"permissionDecisionReason"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Recursive deletes are blocked here. Run git clean -n first, then remove paths individually."&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;PreToolUse&lt;/code&gt; accepts three values:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;th&gt;Effect&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;allow&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Skip the interactive permission prompt. Deny and ask rules still apply&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;deny&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Cancel the tool call and send the reason to Claude&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ask&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Show the permission prompt as normal&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;With &lt;code&gt;deny&lt;/code&gt;, &lt;code&gt;permissionDecisionReason&lt;/code&gt; is fed back to Claude, so writing &lt;em&gt;why&lt;/em&gt; and &lt;em&gt;what to do instead&lt;/em&gt; keeps the work moving rather than just stopping it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;allow cannot override a deny rule&lt;/strong&gt;&lt;br&gt;
The reverse does not hold. A hook returning &lt;code&gt;allow&lt;/code&gt; does not bypass deny rules from settings, and it cannot suppress prompts for MCP tools marked &lt;code&gt;requiresUserInteraction&lt;/code&gt; or for connector tools your organization set to &lt;code&gt;ask&lt;/code&gt;. As the documentation puts it: &lt;strong&gt;hooks can tighten restrictions but not loosen them past what permission rules allow&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Cover file edits too
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;rm&lt;/code&gt; arrives through the Bash tool, but an &lt;code&gt;Edit&lt;/code&gt; or &lt;code&gt;Write&lt;/code&gt; that clobbers an important file is the same class of accident. The documentation shows a path-based guard:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;# protect-files.sh&lt;/span&gt;

&lt;span class="nv"&gt;INPUT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;FILE_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$INPUT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.tool_input.file_path // empty'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Normalize Windows backslash separators so the patterns below match&lt;/span&gt;
&lt;span class="nv"&gt;FILE_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;FILE_PATH&lt;/span&gt;&lt;span class="p"&gt;//\\//&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="nv"&gt;PROTECTED_PATTERNS&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;&lt;span class="s2"&gt;".env"&lt;/span&gt; &lt;span class="s2"&gt;"package-lock.json"&lt;/span&gt; &lt;span class="s2"&gt;".git/"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;pattern &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PROTECTED_PATTERNS&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$FILE_PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$pattern&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Blocked: &lt;/span&gt;&lt;span class="nv"&gt;$FILE_PATH&lt;/span&gt;&lt;span class="s2"&gt; matches protected pattern '&lt;/span&gt;&lt;span class="nv"&gt;$pattern&lt;/span&gt;&lt;span class="s2"&gt;'"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
    &lt;span class="nb"&gt;exit &lt;/span&gt;2
  &lt;span class="k"&gt;fi
done

&lt;/span&gt;&lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Register that one with a &lt;code&gt;matcher&lt;/code&gt; of &lt;code&gt;Edit|Write&lt;/code&gt;. For the shape of the JSON these scripts read, see &lt;a href="https://aicoding-guide.com/en/posts/claude-code-hooks-stdin-json/" rel="noopener noreferrer"&gt;The JSON your Claude Code hooks receive on stdin&lt;/a&gt;; for matcher values, see &lt;a href="https://aicoding-guide.com/en/posts/claude-code-hooks-matcher/" rel="noopener noreferrer"&gt;Every value you can put in a Claude Code hook matcher&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where string matching breaks
&lt;/h2&gt;

&lt;p&gt;This is the part to be honest about. &lt;strong&gt;Matching the command text is easy to slip past.&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Written as&lt;/th&gt;
&lt;th&gt;Matched by a naive &lt;code&gt;rm -rf&lt;/code&gt; check&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rm -rf build&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rm -r -f build&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rm --recursive --force build&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;FLAGS="-rf"; rm $FLAGS build&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;find . -delete&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Not an &lt;code&gt;rm&lt;/code&gt; at all&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The script above handles &lt;code&gt;-rf&lt;/code&gt; and &lt;code&gt;-fr&lt;/code&gt; orderings, and that is still not exhaustive. A hook is an effective guardrail against mistakes; it is &lt;strong&gt;not a mechanism for stopping deliberate evasion&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For anything you need genuinely enforced, layer these underneath:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Permission deny rules&lt;/strong&gt;: the documentation states that hook decisions don't bypass permission rules — Claude Code evaluates deny and ask rules regardless of what a &lt;code&gt;PreToolUse&lt;/code&gt; hook returns. See &lt;a href="https://aicoding-guide.com/en/posts/claude-code-settings-json-permissions/" rel="noopener noreferrer"&gt;Claude Code permissions in settings.json&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The sandbox&lt;/strong&gt;: OS-level restriction that also covers files a script opens indirectly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Worth knowing too: &lt;code&gt;rm&lt;/code&gt; and &lt;code&gt;rmdir&lt;/code&gt; targeting a critical path are never auto-approved in any mode, and no allow rule or &lt;code&gt;PreToolUse&lt;/code&gt; hook &lt;code&gt;allow&lt;/code&gt; approves them.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Glossary&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;PreToolUse&lt;/strong&gt;: the event that fires immediately before a tool runs. Denying here means the tool call never happens.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Checking it works
&lt;/h2&gt;

&lt;p&gt;After registering, run &lt;code&gt;/hooks&lt;/code&gt; and confirm the hook appears under the right event. To test the script by itself, pipe sample JSON into it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'{"tool_name":"Bash","tool_input":{"command":"rm -rf /tmp/x"}}'&lt;/span&gt; | ./.claude/hooks/block-rm-rf.sh&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"exit=&lt;/span&gt;&lt;span class="nv"&gt;$?&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;exit=2&lt;/code&gt; is what you want to see.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;PreToolUse&lt;/code&gt; hooks fire before permission-mode checks, so a &lt;code&gt;deny&lt;/code&gt; holds even in &lt;code&gt;bypassPermissions&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Two ways to block: &lt;code&gt;exit 2&lt;/code&gt; for brevity, &lt;code&gt;permissionDecision&lt;/code&gt; JSON to pass a reason&lt;/li&gt;
&lt;li&gt;Multiple hooks on one event run in parallel, and a deny wins&lt;/li&gt;
&lt;li&gt;A hook's &lt;code&gt;allow&lt;/code&gt; cannot override a deny rule — hooks tighten, they don't loosen&lt;/li&gt;
&lt;li&gt;String matching misses &lt;code&gt;rm -r -f&lt;/code&gt; and variable expansion; use deny rules and the sandbox for real enforcement&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>claudecode</category>
      <category>hooks</category>
      <category>pretooluse</category>
      <category>rmrf</category>
    </item>
    <item>
      <title>Get a desktop notification when Claude Code finishes, with a Stop hook</title>
      <dc:creator>aicoding-guide</dc:creator>
      <pubDate>Sun, 20 Sep 2026 19:07:56 +0000</pubDate>
      <link>https://dev.to/aicoding-guide/get-a-desktop-notification-when-claude-code-finishes-with-a-stop-hook-3832</link>
      <guid>https://dev.to/aicoding-guide/get-a-desktop-notification-when-claude-code-finishes-with-a-stop-hook-3832</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://aicoding-guide.com/en/posts/claude-code-hooks-stop-notify/" rel="noopener noreferrer"&gt;https://aicoding-guide.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Watching a terminal to see whether a long task has finished is wasted time. A notification at the moment it completes frees you to do something else in the meantime.&lt;/p&gt;

&lt;p&gt;The event that fires when Claude finishes responding is the &lt;strong&gt;&lt;code&gt;Stop&lt;/code&gt; hook&lt;/strong&gt;. The &lt;code&gt;Notification&lt;/code&gt; hook in the documentation's getting-started walkthrough is a different thing: it tells you Claude is waiting for input. This article keeps the two apart and gives the setup for all three platforms.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key point&lt;/strong&gt;&lt;br&gt;
What you will learn&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How &lt;code&gt;Stop&lt;/code&gt; differs from &lt;code&gt;Notification&lt;/code&gt;, and which one you want&lt;/li&gt;
&lt;li&gt;The notification command and config for macOS, Linux and Windows&lt;/li&gt;
&lt;li&gt;The exit-code trap that can keep Claude from stopping&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Stop or Notification?
&lt;/h2&gt;

&lt;p&gt;The names are similar; the timing is not.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Event&lt;/th&gt;
&lt;th&gt;Fires when&lt;/th&gt;
&lt;th&gt;Matcher&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Stop&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Claude finishes responding&lt;/td&gt;
&lt;td&gt;Not supported&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SubagentStop&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A subagent finishes&lt;/td&gt;
&lt;td&gt;Matches on agent type&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Notification&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Claude Code sends a notification (permission prompt, idle, and so on)&lt;/td&gt;
&lt;td&gt;Matches on notification type&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;"Tell me when the work is done" is &lt;code&gt;Stop&lt;/code&gt;. "Tell me when it's stuck on a permission prompt" is &lt;code&gt;Notification&lt;/code&gt;. Configuring both is fine.&lt;/p&gt;

&lt;p&gt;The notification types you can match on include &lt;code&gt;permission_prompt&lt;/code&gt;, &lt;code&gt;idle_prompt&lt;/code&gt;, &lt;code&gt;auth_success&lt;/code&gt;, &lt;code&gt;agent_needs_input&lt;/code&gt; and &lt;code&gt;agent_completed&lt;/code&gt;. The full list of matcher values per event is in &lt;a href="https://aicoding-guide.com/en/posts/claude-code-hooks-matcher/" rel="noopener noreferrer"&gt;Every value you can put in a Claude Code hook matcher&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A matcher on Stop is ignored&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;Stop&lt;/code&gt; does not support a matcher. Adding one is not an error — it is silently ignored, and the hook fires every time. To narrow it, branch inside the hook script instead.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Configuration by platform
&lt;/h2&gt;

&lt;p&gt;These go in &lt;code&gt;~/.claude/settings.json&lt;/code&gt;. Create the file if it does not exist.&lt;/p&gt;

&lt;h3&gt;
  
  
  macOS
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Stop"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"osascript -e 'display notification &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Claude Code finished&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; with title &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Claude Code&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;'"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If nothing appears, Script Editor — which &lt;code&gt;osascript&lt;/code&gt; routes notifications through — probably lacks notification permission. The documentation notes that in that case the command fails silently and macOS never prompts you to grant it. Run this once in Terminal so Script Editor shows up in your notification settings, then enable &lt;strong&gt;Allow Notifications&lt;/strong&gt; for &lt;strong&gt;Script Editor&lt;/strong&gt; under &lt;strong&gt;System Settings &amp;gt; Notifications&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;osascript &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'display notification "test"'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Linux
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Stop"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"notify-send 'Claude Code' 'Claude Code finished'"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;notify-send&lt;/code&gt; needs a desktop notification daemon, which headless servers, SSH sessions and most containers do not have. Test the command directly first. If it is not found, install &lt;code&gt;libnotify-bin&lt;/code&gt; on Debian and Ubuntu, or your distribution's equivalent.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;notify-send &lt;span class="s1"&gt;'Claude Code'&lt;/span&gt; &lt;span class="s1"&gt;'test'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Windows (PowerShell)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Stop"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"powershell.exe -Command &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;[System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms'); [System.Windows.Forms.MessageBox]::Show('Claude Code finished', 'Claude Code')&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run &lt;code&gt;/hooks&lt;/code&gt; afterwards to see the configured hooks per event and which file each came from. Note that the &lt;code&gt;/hooks&lt;/code&gt; menu is read-only: to add, modify or remove hooks, edit the settings JSON directly or ask Claude to do it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The exit-code trap
&lt;/h2&gt;

&lt;p&gt;This is the part specific to &lt;code&gt;Stop&lt;/code&gt;. &lt;strong&gt;A &lt;code&gt;Stop&lt;/code&gt; hook that exits with code 2 blocks the stop, and the conversation continues.&lt;/strong&gt; If your notification command fails and returns 2, Claude keeps going when you did not intend it to.&lt;/p&gt;

&lt;p&gt;When all you want is a notification, make sure the command exits 0.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"notify-send 'Claude Code' 'Claude Code finished' || true"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;|| true&lt;/code&gt; turns a failed notification into a 0 exit.&lt;/p&gt;

&lt;p&gt;There is also a cap on blocking. The documentation states that Claude Code overrides a &lt;code&gt;Stop&lt;/code&gt; hook after it blocks &lt;strong&gt;eight times in a row without progress&lt;/strong&gt;. If you write a hook that deliberately blocks, read &lt;code&gt;stop_hook_active&lt;/code&gt; from the JSON on stdin and exit early once you have already triggered a continuation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="nv"&gt;INPUT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$INPUT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.stop_hook_active'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"true"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;exit &lt;/span&gt;0  &lt;span class="c"&gt;# Allow Claude to stop&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;span class="c"&gt;# ... rest of your hook logic&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Putting content in the notification
&lt;/h2&gt;

&lt;p&gt;A &lt;code&gt;Stop&lt;/code&gt; hook's stdin carries &lt;code&gt;last_assistant_message&lt;/code&gt; and &lt;code&gt;stop_reason&lt;/code&gt;, so the notification can say what actually finished.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="c"&gt;# ~/.claude/hooks/notify-done.sh&lt;/span&gt;
&lt;span class="nv"&gt;INPUT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;MSG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$INPUT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.last_assistant_message // empty'&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; 120&lt;span class="si"&gt;)&lt;/span&gt;
notify-send &lt;span class="s1"&gt;'Claude Code'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MSG&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;Claude&lt;/span&gt;&lt;span class="p"&gt; Code finished&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true
exit &lt;/span&gt;0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Stop"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$CLAUDE_PROJECT_DIR&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;/.claude/hooks/notify-done.sh"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the full shape of that JSON, see &lt;a href="https://aicoding-guide.com/en/posts/claude-code-hooks-stdin-json/" rel="noopener noreferrer"&gt;The JSON your Claude Code hooks receive on stdin&lt;/a&gt;. For building hooks generally, see &lt;a href="https://aicoding-guide.com/en/posts/claude-code-hooks-lint-format/" rel="noopener noreferrer"&gt;Run lint and format automatically after every edit&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Glossary&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;stop_reason&lt;/strong&gt;: a string describing why Claude stopped. A normal end of turn carries &lt;code&gt;end_turn&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;Stop&lt;/code&gt; for "work finished" and &lt;code&gt;Notification&lt;/code&gt; for "waiting on you"&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Stop&lt;/code&gt; takes no matcher; one written there is silently ignored&lt;/li&gt;
&lt;li&gt;macOS uses &lt;code&gt;osascript&lt;/code&gt;, Linux &lt;code&gt;notify-send&lt;/code&gt;, Windows a PowerShell MessageBox&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;Stop&lt;/code&gt; hook exiting 2 blocks the stop; append &lt;code&gt;|| true&lt;/code&gt; when you only want a notification&lt;/li&gt;
&lt;li&gt;If you block deliberately, check &lt;code&gt;stop_hook_active&lt;/code&gt; to stay under the eight-block cap&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;last_assistant_message&lt;/code&gt; lets the notification say what finished&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>claudecode</category>
      <category>hooks</category>
      <category>stop</category>
      <category>notifications</category>
    </item>
    <item>
      <title>This week in Claude Code, Codex and Gemini CLI (week of September 20, 2026)</title>
      <dc:creator>aicoding-guide</dc:creator>
      <pubDate>Sat, 19 Sep 2026 19:08:56 +0000</pubDate>
      <link>https://dev.to/aicoding-guide/this-week-in-claude-code-codex-and-gemini-cli-week-of-september-20-2026-2fg1</link>
      <guid>https://dev.to/aicoding-guide/this-week-in-claude-code-codex-and-gemini-cli-week-of-september-20-2026-2fg1</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://aicoding-guide.com/en/posts/weekly-2026-09-20/" rel="noopener noreferrer"&gt;https://aicoding-guide.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The headline this week is that Claude Code now &lt;strong&gt;reads &lt;code&gt;AGENTS.md&lt;/code&gt; directly&lt;/strong&gt; (v2.1.277). Until now you had to import it from a &lt;code&gt;CLAUDE.md&lt;/code&gt;; in a repository without one, it is simply read.&lt;/p&gt;

&lt;p&gt;Second: the 2.1.268 change this digest reported last week as a permission-bypass fix was &lt;strong&gt;reverted in 2.1.273&lt;/strong&gt;. If you relied on that behavior, it is back to what it was.&lt;/p&gt;

&lt;p&gt;Covered here: Claude Code 2.1.270 through 2.1.277, Codex CLI 0.155.0 and 0.155.1, and Gemini CLI 0.60.0.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key point&lt;/strong&gt;&lt;br&gt;
What you will learn&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Claude Code's &lt;code&gt;AGENTS.md&lt;/code&gt; support and which file wins&lt;/li&gt;
&lt;li&gt;The deny-rule fix from last week that got reverted&lt;/li&gt;
&lt;li&gt;Codex voice conversations and Touch ID, plus Gemini CLI's security fixes&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Claude Code
&lt;/h2&gt;

&lt;h3&gt;
  
  
  2.1.277: AGENTS.md support
&lt;/h3&gt;

&lt;p&gt;The changelog states it plainly: &lt;strong&gt;in a project with no CLAUDE.md, Claude Code reads AGENTS.md instead&lt;/strong&gt;. The documentation now has an &lt;code&gt;AGENTS.md&lt;/code&gt; section, and the resolution is this:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Your repository has&lt;/th&gt;
&lt;th&gt;Claude reads&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;An &lt;code&gt;AGENTS.md&lt;/code&gt;, and no &lt;code&gt;CLAUDE.md&lt;/code&gt; or &lt;code&gt;CLAUDE.local.md&lt;/code&gt; in your working directory or above it&lt;/td&gt;
&lt;td&gt;Your &lt;code&gt;AGENTS.md&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;An &lt;code&gt;AGENTS.md&lt;/code&gt; and a &lt;code&gt;CLAUDE.md&lt;/code&gt; or &lt;code&gt;CLAUDE.local.md&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Your &lt;code&gt;CLAUDE.md&lt;/code&gt; files only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A &lt;code&gt;CLAUDE.md&lt;/code&gt; that already imports &lt;code&gt;AGENTS.md&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Your &lt;code&gt;CLAUDE.md&lt;/code&gt;, with &lt;code&gt;AGENTS.md&lt;/code&gt; through the import&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Three files count for that check: &lt;code&gt;CLAUDE.md&lt;/code&gt;, &lt;code&gt;.claude/CLAUDE.md&lt;/code&gt; and &lt;code&gt;CLAUDE.local.md&lt;/code&gt; in your working directory or above. Your &lt;code&gt;~/.claude/CLAUDE.md&lt;/code&gt;, your organization's managed &lt;code&gt;CLAUDE.md&lt;/code&gt; and &lt;code&gt;.claude/rules/&lt;/code&gt; files do not count and keep loading alongside &lt;code&gt;AGENTS.md&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To change the default, run &lt;code&gt;/config&lt;/code&gt; and set &lt;strong&gt;Project instructions&lt;/strong&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;th&gt;What Claude reads&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;claude-md-or-agents-md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The default: &lt;code&gt;CLAUDE.md&lt;/code&gt;, or &lt;code&gt;AGENTS.md&lt;/code&gt; when you have none&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;claude-md-and-agents-md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Both, each directory's &lt;code&gt;CLAUDE.md&lt;/code&gt; first and its &lt;code&gt;AGENTS.md&lt;/code&gt; after&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;claude-md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;CLAUDE.md&lt;/code&gt; only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;managed-only&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Only your organization's managed &lt;code&gt;CLAUDE.md&lt;/code&gt; and auto memory&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;You can set it in a settings file instead, under the built-in &lt;code&gt;agents-md&lt;/code&gt; plugin's ID. Project and local settings files are ignored for this value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"pluginConfigs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"agents-md@builtin"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"options"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"instructionFiles"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"claude-md-and-agents-md"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Some sessions don't get this&lt;/strong&gt;&lt;br&gt;
The documentation lists the cases where Claude reads &lt;code&gt;CLAUDE.md&lt;/code&gt; only and &lt;strong&gt;Project instructions&lt;/strong&gt; doesn't appear in &lt;code&gt;/config&lt;/code&gt;: a version before v2.1.277; a session that doesn't fetch feature flags from Anthropic, such as Amazon Bedrock or another third-party provider or with telemetry disabled; your first session after installing or upgrading; and setting &lt;code&gt;disableAllHooks&lt;/code&gt; or &lt;code&gt;allowManagedHooksOnly&lt;/code&gt;, or disabling the built-in &lt;code&gt;agents-md&lt;/code&gt; plugin. Import &lt;code&gt;AGENTS.md&lt;/code&gt; from a &lt;code&gt;CLAUDE.md&lt;/code&gt; in those environments.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For how the three tools' context files relate, see &lt;a href="https://aicoding-guide.com/en/posts/context-files-comparison/" rel="noopener noreferrer"&gt;CLAUDE.md vs AGENTS.md vs GEMINI.md&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.1.276: upgrade now if you use a gateway
&lt;/h3&gt;

&lt;p&gt;A 2.1.275 regression made &lt;strong&gt;every request fail with &lt;code&gt;400 … Input tag 'advisor_20260301'&lt;/code&gt;&lt;/strong&gt; when &lt;code&gt;ANTHROPIC_BASE_URL&lt;/code&gt; points at a proxy or gateway. 2.1.276 fixes it. If that describes your setup, upgrading is mandatory.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.1.275: a send-now key and claude.ai syncing
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Send-now key&lt;/strong&gt;: &lt;code&gt;ctrl+enter&lt;/code&gt; (or &lt;code&gt;ctrl+x ctrl+s&lt;/code&gt;) interrupts the current turn and sends all queued messages at once&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Skill and plugin syncing&lt;/strong&gt;: the skills and plugins enabled on your claude.ai account now sync to terminal sessions signed in with it. Opt out with &lt;code&gt;syncClaudeAiSkills: false&lt;/code&gt; or &lt;code&gt;syncClaudeAiPlugins: false&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/plugin install &amp;lt;plugin&amp;gt; --marketplace &amp;lt;source&amp;gt;&lt;/code&gt; offers to add the marketplace before installing&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2.1.273: last week's deny-rule fix was reverted
&lt;/h3&gt;

&lt;p&gt;Last week this digest reported that Read and Edit deny rules failing to apply on a line containing &lt;code&gt;env -C&lt;/code&gt; or &lt;code&gt;eval&lt;/code&gt; had been fixed. That &lt;strong&gt;2.1.268 change was reverted in 2.1.273&lt;/strong&gt;. The changelog reads: "Reverted a 2.1.268 change that checked Read and Edit deny rules on Bash lines the permission checker can't analyze". Commands like &lt;code&gt;time -p make build&lt;/code&gt; prompt again rather than being denied.&lt;/p&gt;

&lt;p&gt;Permission fixes did land in the same release, though:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fixed Bash commands the permission checker cannot fully analyze skipping the prompt under &lt;code&gt;permissions.blockReadsOutsideWorkingDirectories&lt;/code&gt;, and a subshell hiding a dangerous &lt;code&gt;rm&lt;/code&gt; in bypass mode&lt;/li&gt;
&lt;li&gt;Fixed the context meter and auto-compact counting advisor-tool turns at &lt;strong&gt;roughly twice&lt;/strong&gt; their real context size, which made auto-compact fire at about half the real window&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The first one bears directly on protecting &lt;code&gt;.env&lt;/code&gt; with deny rules — see &lt;a href="https://aicoding-guide.com/en/posts/claude-code-deny-read-env/" rel="noopener noreferrer"&gt;Stop Claude Code reading your .env with a Read deny rule&lt;/a&gt;. For compaction thresholds, see &lt;a href="https://aicoding-guide.com/en/posts/claude-code-autocompact-window/" rel="noopener noreferrer"&gt;autoCompactWindow&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.1.274: MCP fixes
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Fixed Streamable HTTP MCP tool calls &lt;strong&gt;timing out after about 5 minutes&lt;/strong&gt; even with a longer per-server &lt;code&gt;timeout&lt;/code&gt; set&lt;/li&gt;
&lt;li&gt;Added &lt;code&gt;CLAUDE_CODE_MCP_STARTUP_WAIT_MS&lt;/code&gt; to bound how long the first non-interactive turn waits for connecting MCP servers (&lt;code&gt;0&lt;/code&gt; means don't wait)&lt;/li&gt;
&lt;li&gt;Fixed MCP servers configured as &lt;code&gt;http&lt;/code&gt; that only speak legacy HTTP+SSE failing to connect when they answer the first request with 422 or another 4xx&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For MCP configuration, see &lt;a href="https://aicoding-guide.com/en/posts/claude-code-mcp-servers-setup/" rel="noopener noreferrer"&gt;Adding MCP servers to Claude Code&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.1.271 and 2.1.270
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Per-command &lt;code&gt;allowed_domains&lt;/code&gt;&lt;/strong&gt; for Bash, PowerShell and Monitor in auto mode with sandboxing: the hosts a command needs are reviewed with it and opened for it alone, and other hosts are refused&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;omitClaudeMd&lt;/code&gt;&lt;/strong&gt; in agent frontmatter and &lt;code&gt;--agents&lt;/code&gt; JSON lets custom and plugin subagents run without user, project and local &lt;code&gt;CLAUDE.md&lt;/code&gt; files. Managed policy files still load&lt;/li&gt;
&lt;li&gt;Several Bash permission-check gaps closed: files a wildcard expands to, files read by &lt;code&gt;fmt&lt;/code&gt; and &lt;code&gt;column&lt;/code&gt; after an unrecognized option, shell variable declaration flags misrepresenting the command, and commands with two directory changes&lt;/li&gt;
&lt;li&gt;2.1.270 fixed a 2.1.269 regression where read-only git commands started asking for permission after a session had been running a while&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Codex
&lt;/h2&gt;

&lt;h3&gt;
  
  
  CLI 0.155.0
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Experimental &lt;code&gt;/voice&lt;/code&gt; conversations&lt;/strong&gt; with live transcripts and microphone controls, enabled through &lt;code&gt;/experimental&lt;/code&gt; on supported builds&lt;/li&gt;
&lt;li&gt;The TUI shows live reasoning summaries in the status row and completion timestamps after successful turns&lt;/li&gt;
&lt;li&gt;Task hiding, archiving and deletion in the agents overview, plus worktree ownership details and confirmed deletion of clean managed worktrees&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Touch ID verification for MCP requests&lt;/strong&gt; in local TUI sessions on supported Macs&lt;/li&gt;
&lt;li&gt;Amazon Bedrock can obtain AWS credentials from configured commands, with caching and expiration-based refresh&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On the fixes side, Windows-process escapes from restricted WSL sandboxes are now blocked, and brokered shell snapshots were hardened against credential exposure.&lt;/p&gt;

&lt;h3&gt;
  
  
  CLI 0.155.1
&lt;/h3&gt;

&lt;p&gt;New local TUI sessions leave reasoning summaries disabled by default again, fixing request rejection by providers that do not support them. Explicit reasoning-summary settings are still respected.&lt;/p&gt;

&lt;p&gt;For how approvals and the sandbox interact, see &lt;a href="https://aicoding-guide.com/en/posts/codex-full-auto/" rel="noopener noreferrer"&gt;What does Codex --full-auto actually do?&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gemini CLI
&lt;/h2&gt;

&lt;h3&gt;
  
  
  0.60.0
&lt;/h3&gt;

&lt;p&gt;Nearly every line in the release notes is a security fix.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Area&lt;/th&gt;
&lt;th&gt;Change&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Web fetch&lt;/td&gt;
&lt;td&gt;Improved destination validation and connection routing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MCP OAuth&lt;/td&gt;
&lt;td&gt;Enforces RFC 9207 issuer identification&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;macOS Seatbelt&lt;/td&gt;
&lt;td&gt;Isolates the temporary directory for the sandbox&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Extensions&lt;/td&gt;
&lt;td&gt;Hardened path resolution and boundary validation; prompts for consent on environment changes and sanitizes runtime-altering environment variables&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;chrome-devtools-mcp&lt;/td&gt;
&lt;td&gt;Removed a hardcoded Google CrUX API key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Workspace boundaries&lt;/td&gt;
&lt;td&gt;Stronger boundary checks and symlink resolution in command safety and file discovery&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;System config&lt;/td&gt;
&lt;td&gt;Strict permission and ownership checks on system-wide configuration paths&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Windows&lt;/td&gt;
&lt;td&gt;Mitigates NTFS 8.3 short name (SFN) paths&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Glossary&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;NTFS 8.3 short names&lt;/strong&gt;: the &lt;code&gt;PROGRA~1&lt;/code&gt;-style aliases Windows generates for long filenames. Because the same file can be reached by a second spelling, they are a route around path checks.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What to check now
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;[ ] On 2.1.275 behind a proxy or gateway, upgrade to 2.1.276 or later&lt;/li&gt;
&lt;li&gt;[ ] In repositories with an &lt;code&gt;AGENTS.md&lt;/code&gt; and no &lt;code&gt;CLAUDE.md&lt;/code&gt;, know that v2.1.277 changes what gets loaded&lt;/li&gt;
&lt;li&gt;[ ] If you keep both files, note that &lt;code&gt;AGENTS.md&lt;/code&gt; is not read by default; use &lt;code&gt;claude-md-and-agents-md&lt;/code&gt; if you want both&lt;/li&gt;
&lt;li&gt;[ ] If you relied on deny rules covering &lt;code&gt;eval&lt;/code&gt; or &lt;code&gt;env -C&lt;/code&gt; lines, revisit that assumption for 2.1.273 and later&lt;/li&gt;
&lt;li&gt;[ ] If auto-compact was firing early, check whether 2.1.273 improved it&lt;/li&gt;
&lt;li&gt;[ ] To keep claude.ai skills and plugins out of terminal sessions, set &lt;code&gt;syncClaudeAiSkills&lt;/code&gt; and &lt;code&gt;syncClaudeAiPlugins&lt;/code&gt; to &lt;code&gt;false&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Claude Code v2.1.277 reads &lt;code&gt;AGENTS.md&lt;/code&gt; directly, in repositories that have no &lt;code&gt;CLAUDE.md&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The default is &lt;code&gt;claude-md-or-agents-md&lt;/code&gt;; change &lt;strong&gt;Project instructions&lt;/strong&gt; in &lt;code&gt;/config&lt;/code&gt; to load both&lt;/li&gt;
&lt;li&gt;The 2.1.268 deny-rule change was reverted in 2.1.273, correcting what this digest said last week&lt;/li&gt;
&lt;li&gt;2.1.275 has a gateway regression that fails every request with a 400; 2.1.276 fixes it&lt;/li&gt;
&lt;li&gt;Codex 0.155.0 adds experimental &lt;code&gt;/voice&lt;/code&gt; and Touch ID for MCP requests; 0.155.1 restores the reasoning-summary default&lt;/li&gt;
&lt;li&gt;Gemini CLI 0.60.0 is almost entirely security work, including RFC 9207 enforcement in MCP OAuth&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>weeklydigest</category>
      <category>updates</category>
      <category>claudecode</category>
      <category>codex</category>
    </item>
    <item>
      <title>Stop Claude Code reading your .env with a Read deny rule</title>
      <dc:creator>aicoding-guide</dc:creator>
      <pubDate>Fri, 18 Sep 2026 19:10:05 +0000</pubDate>
      <link>https://dev.to/aicoding-guide/stop-claude-code-reading-your-env-with-a-read-deny-rule-5f16</link>
      <guid>https://dev.to/aicoding-guide/stop-claude-code-reading-your-env-with-a-read-deny-rule-5f16</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://aicoding-guide.com/en/posts/claude-code-deny-read-env/" rel="noopener noreferrer"&gt;https://aicoding-guide.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Your &lt;code&gt;.env&lt;/code&gt; holds API keys and database passwords. Claude Code reads files inside the working directory without asking, so by default the contents of &lt;code&gt;.env&lt;/code&gt; end up in the model's input.&lt;/p&gt;

&lt;p&gt;The reliable way to stop that is a &lt;code&gt;Read&lt;/code&gt; deny rule in &lt;code&gt;settings.json&lt;/code&gt;. Deny rules are evaluated before allow rules and block regardless of permission mode.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key point&lt;/strong&gt;&lt;br&gt;
What you will learn&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The deny rule that keeps &lt;code&gt;.env&lt;/code&gt; out of reach&lt;/li&gt;
&lt;li&gt;How pattern shape changes what the rule actually covers&lt;/li&gt;
&lt;li&gt;Where deny rules do not reach, and what to use instead&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The rule
&lt;/h2&gt;

&lt;p&gt;Put this in your project's &lt;code&gt;.claude/settings.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"permissions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"deny"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Read(./.env)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Read(./.env.*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Read(./secrets/**)"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Read(./.env.*)&lt;/code&gt; covers derived files such as &lt;code&gt;.env.local&lt;/code&gt; and &lt;code&gt;.env.production&lt;/code&gt;. A &lt;code&gt;Read&lt;/code&gt; deny rule also blocks the Edit and Write tools on the same path, including creating a new file there, so these three lines stop both reading and rewriting. NotebookEdit is not covered — add an &lt;code&gt;Edit&lt;/code&gt; deny rule for paths no tool may change.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Glossary&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Deny precedence&lt;/strong&gt;: rules are evaluated in order — deny, then ask, then allow — and the first match determines the outcome. Rule specificity does not change that order, so an allow rule cannot carve an exception out of a deny rule.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Pattern shape decides the anchor
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Read&lt;/code&gt; and &lt;code&gt;Edit&lt;/code&gt; rules use gitignore pattern syntax. The leading characters decide where the pattern is anchored.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pattern&lt;/th&gt;
&lt;th&gt;Anchored at&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;path&lt;/code&gt; or &lt;code&gt;./path&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Current directory&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Read(./.env)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/path&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The settings source that defines it&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Read(/config/.env)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;~/path&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Home directory&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Read(~/.aws/credentials)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;//path&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Filesystem root&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Read(//etc/shadow)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Depth is a separate question. Bare filenames follow gitignore semantics and match at any depth:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Deny rule&lt;/th&gt;
&lt;th&gt;Blocks&lt;/th&gt;
&lt;th&gt;Does not block&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Read(./.env)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;.env&lt;/code&gt; in the current directory&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;.env&lt;/code&gt; in a subdirectory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;Read(.env)&lt;/code&gt; or &lt;code&gt;Read(**/.env)&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;any &lt;code&gt;.env&lt;/code&gt; at or under the current directory&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;.env&lt;/code&gt; in a parent directory or another project&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Read(//**/.env)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;any &lt;code&gt;.env&lt;/code&gt; anywhere on the filesystem&lt;/td&gt;
&lt;td&gt;nothing; it is anchored at the filesystem root&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In a monorepo where &lt;code&gt;packages/api/.env&lt;/code&gt; and &lt;code&gt;packages/web/.env&lt;/code&gt; both exist, &lt;code&gt;Read(./.env)&lt;/code&gt; is not enough. Write &lt;code&gt;Read(.env)&lt;/code&gt; to match at any depth.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The anchor moves when you write the rule in user settings&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;Read(/secrets/**)&lt;/code&gt; in &lt;code&gt;~/.claude/settings.json&lt;/code&gt; anchors at the settings source, so it matches &lt;code&gt;~/.claude/secrets/**&lt;/code&gt; — not your project's &lt;code&gt;secrets&lt;/code&gt; directory. A single leading slash is not an absolute path. To cover every project, use &lt;code&gt;//&lt;/code&gt; for a filesystem-absolute path or &lt;code&gt;~/&lt;/code&gt; for a home-relative one.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Where deny rules reach
&lt;/h2&gt;

&lt;p&gt;Read and Edit deny rules apply to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Claude's built-in file tools (Read, Edit, Write), with Grep, Glob, &lt;code&gt;@file&lt;/code&gt; mentions in your prompt and IDE-shared open files covered where possible&lt;/li&gt;
&lt;li&gt;File commands Claude Code recognizes in Bash: &lt;code&gt;cat&lt;/code&gt;, &lt;code&gt;head&lt;/code&gt;, &lt;code&gt;tail&lt;/code&gt;, &lt;code&gt;sed&lt;/code&gt; and &lt;code&gt;tee&lt;/code&gt;. These normally run without a prompt, but a deny rule blocks them&lt;/li&gt;
&lt;li&gt;Bash redirection targets, both &lt;code&gt;&amp;gt; file&lt;/code&gt; and &lt;code&gt;&amp;lt; file&lt;/code&gt;. Input-target checking requires Claude Code v2.1.257 or later&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Symlinks are checked on both paths — the link and its target — and a deny rule applies when either one matches.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Scripts that open files themselves are not covered&lt;/strong&gt;&lt;br&gt;
Deny rules do not apply to a command that reads files without naming them, such as &lt;code&gt;grep -r pattern .&lt;/code&gt; run from the directory holding the file, or to a Python or Node script that opens files itself. &lt;code&gt;python -c "print(open('.env').read())"&lt;/code&gt; is just a command execution as far as the permission checker is concerned. For OS-level enforcement that blocks all processes, enable the sandbox.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Note also that the protected paths list — the set of writes never auto-approved — includes &lt;code&gt;.envrc&lt;/code&gt;, not &lt;code&gt;.env&lt;/code&gt;, and covers writes only. Reads need your own deny rule.&lt;/p&gt;

&lt;h2&gt;
  
  
  Confirming it works
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Save the settings and start Claude Code.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;/permissions&lt;/code&gt;. It lists the active rules and which settings file each came from.&lt;/li&gt;
&lt;li&gt;Ask Claude to read &lt;code&gt;.env&lt;/code&gt;. You should get &lt;code&gt;File is covered by a Read deny rule in your permission settings&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you wrote a pattern that can't be used for file checks, Claude Code warns at startup with &lt;code&gt;... is not matched by file permission checks&lt;/code&gt;. That warning means the path syntax needs fixing — for example, a path rule written for &lt;code&gt;Write&lt;/code&gt;, &lt;code&gt;NotebookEdit&lt;/code&gt; or &lt;code&gt;Glob&lt;/code&gt;, which Claude Code accepts but never consults.&lt;/p&gt;

&lt;p&gt;For the wider rule design, see &lt;a href="https://aicoding-guide.com/en/posts/claude-code-settings-json-permissions/" rel="noopener noreferrer"&gt;Claude Code permissions in settings.json&lt;/a&gt;. For keeping secrets away from all three tools, see &lt;a href="https://aicoding-guide.com/en/posts/ai-coding-security-secrets/" rel="noopener noreferrer"&gt;Stop AI coding tools from reading your API keys and secrets&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Put &lt;code&gt;Read(./.env)&lt;/code&gt; and &lt;code&gt;Read(./.env.*)&lt;/code&gt; in &lt;code&gt;permissions.deny&lt;/code&gt; in &lt;code&gt;.claude/settings.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;Read&lt;/code&gt; deny also covers Edit and Write, but not NotebookEdit&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Read(./.env)&lt;/code&gt; is current-directory only; &lt;code&gt;Read(.env)&lt;/code&gt; matches at any depth&lt;/li&gt;
&lt;li&gt;Deny beats allow and applies in every mode, including &lt;code&gt;bypassPermissions&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Scripts that open files themselves slip past it — use the sandbox for OS-level enforcement&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>claudecode</category>
      <category>env</category>
      <category>deny</category>
      <category>permissions</category>
    </item>
  </channel>
</rss>
