Claude Code notifications on Mac: 5 ways to know it's done

Set up Claude Code notifications on a Mac: desktop alerts in iTerm2, Ghostty and Kitty, the terminal bell, a sound when done, banners and tmux fixes.

By The IdlePal team · Published · Last checked against the official docs

To get Claude Code notifications on a Mac, run it in Ghostty, Kitty or iTerm2, which show a desktop notification with almost no setup, or set preferredNotifChannel to "terminal_bell" in any other terminal. For a custom sound or banner, add a Stop or Notification hook to ~/.claude/settings.json.

Claude Code fires its built-in notification when it finishes a task or pauses for a permission prompt, and you appear to be away from the terminal. Below are five setups, from zero config to a hook that plays one sound for “done” and another for “needs you”, plus the tmux and SSH details that usually break them.

Which Claude Code notifications work out of the box?

Claude Code sends a desktop notification by default in three terminals: Ghostty, Kitty and iTerm2. Ghostty and Kitty forward it to macOS Notification Center without further setup. iTerm2 needs forwarding turned on once:

  1. Open iTerm2 Settings → Profiles → Terminal.
  2. Check Notification Center Alerts.
  3. Click Filter Alerts and enable Send escape sequence-generated alerts.
  4. Open macOS System Settings → Notifications, find iTerm2 and make sure notifications are allowed.

This built-in Claude Code notification is the one to try first, because it also reaches your local Mac when Claude Code runs on a remote machine over SSH. The setting behind it is preferredNotifChannel, which shows up in /config as Local notifications. Its default, "auto", picks the right behavior per terminal. You can also force "iterm2", "iterm2_with_bell", "kitty" or "ghostty", or turn it off with "notifications_disabled".

Ring the terminal bell in Apple Terminal, Warp or VS Code

Every other terminal, including Apple Terminal, Warp and the VS Code or Cursor integrated terminals, gets nothing by default. The quickest fix is the terminal bell:

  1. Open ~/.claude/settings.json (create it if it doesn’t exist).
  2. Add the setting:
{
  "preferredNotifChannel": "terminal_bell"
}
  1. Save. Claude Code now rings the bell character in any terminal when it would have sent a notification.

One catch in Apple Terminal: if you accepted Claude Code’s first-run terminal setup, it ran /terminal-setup, which turns off the audible bell in your Terminal profile. Turn it back on under Settings → Profiles → Advanced → Audible bell.

The bell is a single generic beep. If you want a specific sound, a banner, or a different alert for “done” and “needs you”, you need hooks.

Stop vs Notification hooks: when each one fires

Hooks run a shell command when something happens in a Claude Code session. They run alongside the built-in notification, not instead of it, and they work in every terminal. Two events matter for alerts, and they behave differently.

Stop runs every time the main agent finishes responding. It does not run when you interrupt Claude, and an API error fires StopFailure instead. It doesn’t care whether you’re watching, so a Stop hook goes off even when you’re sitting right there.

Notification runs when Claude Code sends a notification, and its matcher filters by notification type. The two you’ll use:

Matcher When it fires
permission_prompt Claude needs you to approve a tool use, and the prompt has waited about six seconds. Each keystroke resets the timer.
idle_prompt Claude finished responding about 60 seconds ago and you haven’t typed since.

Omit the matcher to catch every notification type. Notification hooks fire even with preferredNotifChannel set to "notifications_disabled", because that setting only changes how you’re alerted.

If you want a hook the instant Claude asks for permission, without the six-second wait, use the PermissionRequest event instead.

A Claude Code sound when done

The simplest Claude Code notifier is afplay, which ships with macOS and plays any sound file. This setup plays Glass every time Claude finishes:

  1. Open ~/.claude/settings.json.
  2. Add a hooks block. If the file already has one, add the Stop entry inside it, since JSON can’t have two hooks keys.
{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          { "type": "command", "command": "afplay /System/Library/Sounds/Glass.aiff" }
        ]
      }
    ]
  }
}
  1. Save. Claude Code picks up edits to settings files automatically.
  2. Run /hooks in Claude Code and check the hook is listed under Stop.
  3. Send any prompt. You should hear Glass when the reply ends.

Any file in /System/Library/Sounds/ works: Basso, Blow, Bottle, Frog, Funk, Glass, Hero, Morse, Ping, Pop, Purr, Sosumi, Submarine and Tink.

A different sound when it needs you

A sound on every finished reply can get noisy. A better split is one sound for “done” and another for “needs you”:

  1. Keep the Stop hook above for “done”.
  2. Add a Notification hook with the matcher permission_prompt|idle_prompt for “needs you”:
{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          { "type": "command", "command": "afplay /System/Library/Sounds/Glass.aiff" }
        ]
      }
    ],
    "Notification": [
      {
        "matcher": "permission_prompt|idle_prompt",
        "hooks": [
          { "type": "command", "command": "afplay /System/Library/Sounds/Sosumi.aiff" }
        ]
      }
    ]
  }
}
  1. Run /hooks again to confirm both events show a hook.

If you only want alerts when you’ve walked away, drop Stop and keep the Notification hook. It inherits the “you appear to be away” timing of the built-in notification.

A desktop banner with osascript

To get a banner in any terminal, including Warp and VS Code, run osascript from a hook:

  1. Add this Notification hook to ~/.claude/settings.json:
{
  "hooks": {
    "Notification": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "osascript -e 'display notification \"Claude Code needs your attention\" with title \"Claude Code\"'"
          }
        ]
      }
    ]
  }
}
  1. In Terminal, run osascript -e 'display notification "test"' once. Nothing appears yet. This only makes Script Editor show up in your notification settings.
  2. Open System Settings → Notifications, find Script Editor and turn on Allow Notifications.
  3. Run the test command again. A banner should appear.

osascript sends notifications through Script Editor, and without that permission it fails silently, which is the most common reason this hook “does nothing”. For a “done” banner, put the same command under Stop with your own text, such as "Claude finished".

Claude Code notifications in tmux and over SSH

tmux. By default, desktop notifications never reach the outer terminal from inside tmux. Fix it once:

  1. Add this line to ~/.tmux.conf:
set -g allow-passthrough on
  1. Run tmux source-file ~/.tmux.conf to apply it to the running server.

SSH. The built-in desktop notification travels back over SSH, so Ghostty, Kitty and iTerm2 still alert you on your Mac. Hook commands are different: they run on the machine where Claude Code runs, so afplay and osascript on a remote Linux box won’t make a sound on your laptop.

For remote sessions, have the hook return a terminalSequence instead. Claude Code writes that escape sequence through its own terminal output, so it reaches your local terminal and also works inside tmux and GNU screen. This script, adapted from the hooks reference, sends an OSC 777 notification (Ghostty and Warp; iTerm2 uses OSC 9 and Kitty uses OSC 99):

#!/bin/bash
input=$(cat)
body=$(jq -r '.message // "Needs your attention"' <<<"$input")
seq=$(printf '\033]777;notify;%s;%s\007' "Claude Code" "$body")
jq -nc --arg seq "$seq" '{terminalSequence: $seq}'

Save it on the remote machine, run chmod +x on it, and point a Notification hook’s command at its absolute path. It needs jq installed.

Troubleshooting Claude Code notifications

If no notification appears, work down this list:

  1. Check you were “away”. The built-in notification, permission_prompt and idle_prompt only fire when you appear to be away from the terminal. Typing resets the six-second timer, and idle_prompt waits about 60 seconds. Test by sending a prompt and not touching the keyboard.
  2. Check macOS permission. In System Settings → Notifications, your terminal app (or Script Editor, for osascript) must be allowed to send notifications. A Focus mode can also hide them.
  3. Check iTerm2’s filter. Without Send escape sequence-generated alerts, iTerm2 drops the notification.
  4. Check the Apple Terminal bell. /terminal-setup turns it off; turn Audible bell back on.
  5. Check the hook is loaded. Run /hooks and confirm it’s under the right event. Matchers are case-sensitive, and settings files can’t contain trailing commas or comments. If an edit doesn’t show up after a few seconds, restart the session.
  6. Check what the hook did. Press Ctrl+O for the transcript view and look for a hook error, or start Claude Code with claude --debug-file /tmp/claude.log and tail -f /tmp/claude.log in another terminal.
  7. Check tmux. Without allow-passthrough, notifications stop at tmux.

A pet in your notch, with IdlePal

A notification tells you Claude is done. IdlePal gives you something to do while it isn’t. It’s a small Mac app: when Claude Code starts working, Pip, a pet that lives in your MacBook notch, wakes up and a panel with a daily puzzle drops down. When Claude finishes, the panel tucks itself away, or tells you if you’re mid-game. When Claude needs your input while you play, the panel tells you that too.

It uses the same hooks as the setups above, packaged as a free plugin that goes live with the app:

claude plugin marketplace add initiums/idlepal-integrations
claude plugin install idlepal@idlepal

The plugin only tells the app “started”, “finished” or “needs you”, plus a session id. It never reads your prompts or code. The Claude Code setup guide has the details, and if you also use Codex, the Codex hooks guide covers the same alerts there.

Which Claude Code notifier should you use?

Setup Effort Fires on Works in Best for
Built-in desktop notification None in Ghostty or Kitty, one setting in iTerm2 Done or needs you, when you’re away Ghostty, Kitty, iTerm2, also over SSH Most people
preferredNotifChannel: "terminal_bell" One line Same as built-in Any terminal Apple Terminal, Warp, VS Code
Stop hook with afplay A few lines of JSON Every finished reply Any terminal on your Mac A sound when done, every time
Notification hook with afplay or osascript A few lines of JSON Permission or idle prompt Any terminal on your Mac A distinct “needs you” alert
IdlePal Install the app and plugin Started, finished, needs you Any terminal on your Mac Something to do during the wait

They stack. A common setup is the built-in notification in iTerm2 plus a quiet Stop sound, so you hear “done” at your desk and get a banner when you’re in another app.

Claude code notifications: questions

How do I turn on Claude Code notifications in iTerm2?

Open Settings → Profiles → Terminal, check Notification Center Alerts, then click Filter Alerts and enable Send escape sequence-generated alerts. Claude Code already sends the notification in iTerm2; this setting lets iTerm2 pass it to macOS.

Why don't I get Claude Code notifications in Warp or the VS Code terminal?

By default Claude Code sends desktop notifications only in Ghostty, Kitty and iTerm2. In other terminals, set preferredNotifChannel to "terminal_bell" in ~/.claude/settings.json, or add a Notification or Stop hook that plays a sound.

How do I get a Claude Code sound when done with every task?

Add a Stop hook to ~/.claude/settings.json whose command is afplay /System/Library/Sounds/Glass.aiff. Stop runs every time Claude finishes responding, except when you interrupt it.

Do Claude Code notifications work inside tmux?

Only after you add set -g allow-passthrough on to ~/.tmux.conf and run tmux source-file ~/.tmux.conf. Without it, tmux swallows the notification before it reaches your terminal.

Does a Claude Code notification hook still run if I turn notifications off?

Yes. The preferredNotifChannel setting, including notifications_disabled, only changes how Claude Code alerts you. Notification hooks still run.

Get Pip first.

One email when IdlePal is on the Mac App Store. Nothing else.

Buttondown opens in a new tab to confirm. Unsubscribe any time.

macOS 14 or later. Best on a MacBook with a notch.