Guide
How to get Claude Code notifications on macOS
Claude Code can tell you when it finishes or needs permission, but on a Mac it only does so through a channel your terminal actually supports. There are three levels: flip the built-in setting, write a Notification hook that posts a real macOS banner, or use something that shows the request itself. This guide covers all three.
Why you are getting nothing right now
Claude Code does not draw macOS notifications itself. It hands the notification to the terminal it is running inside, and terminals differ wildly in whether they can post one. iTerm2, Ghostty and Kitty have a native notification path. Terminal.app does not. So the default behaviour looks broken in one terminal and perfect in another, with no setting changed between them.
That is the whole mystery. Once you know which of the three routes below fits your terminal, it takes about a minute.
Level 1: the built-in setting
The setting is preferredNotifChannel. You can reach it from inside Claude Code with the /config command, or write it directly into your user settings file at ~/.claude/settings.json. It is a user-level setting, not a per-project one.
// ~/.claude/settings.json
{
"preferredNotifChannel": "auto"
}The values worth knowing:
autopicks the best channel your terminal offers. In iTerm2, Ghostty and Kitty that means a real desktop notification. In a terminal with no such channel it has nothing to use.terminal_bellrings the bell. This is the one that works everywhere, including Terminal.app, but the bell has to be enabled in your terminal's own profile settings or you will hear and see nothing.iterm2,iterm2_with_bell,kittyandghosttypin the channel to a specific terminal instead of lettingautodecide.notifications_disabledturns the whole thing off.
If you are on Terminal.app and want more than a bell, skip to level 2. No value of this setting will produce a macOS banner there, because the channel does not exist.
One more piece: whatever the channel, macOS itself has to be willing to show the banner. Check System Settings, Notifications, and confirm your terminal app is allowed to notify and is not being silenced by a Focus mode.
Level 2: a Notification hook that posts a real banner
Claude Code fires a Notification hook event whenever it would notify you, and a hook is just a shell command. That means you can post a proper macOS notification from any terminal, including ones with no notification channel of their own, by shelling out to osascript.
The hook receives the event as JSON on stdin. The fields you care about are message, the notification text, and title. Write a tiny script that reads them and hands them to AppleScript:
#!/bin/bash
# ~/.claude/notify.sh - chmod +x this after saving
input=$(cat)
msg=$(echo "$input" | /usr/bin/python3 -c \
'import json,sys; print(json.load(sys.stdin).get("message",""))')
/usr/bin/osascript -e "display notification \"$msg\" with title \"Claude Code\""Then register it in your settings file. Hooks are grouped by event name, and each group is a list of matcher blocks:
// ~/.claude/settings.json
{
"hooks": {
"Notification": [
{
"hooks": [
{ "type": "command", "command": "$HOME/.claude/notify.sh" }
]
}
]
}
}Restart Claude Code, or use the /hooks command to check that it registered. The first time the script runs, macOS will ask whether the script runner may post notifications; allow it, or nothing will appear.
The Notification event carries a type telling you what kind of notification it is, so you can match only the ones you care about. Waiting for permission and finished-and-idle are separate types, and you will probably want different sounds for them. There is a full explanation of matchers and the rest of the hook lifecycle in the hooks guide.
The thing notifications cannot do
Both levels above solve the same half of the problem: knowing. Neither solves the other half. A banner tells you Claude Code is waiting; you still have to find the window, find the tab, read the command, and press a key. If you are running three or four sessions at once, the banner does not even tell you which one it came from without extra work in your script.
That gap is worth naming clearly, because it is where most DIY notification setups quietly stop being enough.
Level 3: show the request, not a notice about it
Noveriq is a native macOS app that takes the other approach. Instead of notifying you that a session is waiting, it puts the session itself in the MacBook notch: what it is doing, how long it has been going, and the exact command it is asking to run. You press Allow or Deny there.
It can do that because a Claude Code permission hook blocks on a local Unix socket while it waits for a decision, and Noveriq answers over that same socket. Your terminal never comes forward and nothing is typed into it. There is deliberately no synthetic-keystroke path in the app.
Honest limit: what Noveriq can do for a given session depends on what that agent exposes. A visible session does not imply it can answer that session's prompts. When an agent offers no decision channel, the fallback is Jump, which focuses the exact terminal window, tab or split pane instead of leaving you to hunt for it.
Hooks are configured on first launch, so there is no settings file to write by hand and nothing to undo later. It costs $9.99 once for one Mac and needs macOS 15 or later. Details on the Claude Code page.
Which one to pick
- You use iTerm2, Ghostty or Kitty and mostly run one session. The built-in setting is enough. Set it to
autoand stop there. - You use Terminal.app, or you want the notification text to say something specific. Write the hook. It is fifteen lines and it is yours.
- You run several sessions and the switching is what hurts. A notification will not fix that; look at reducing the number of prompts first, and at a panel that can answer them second.
Questions
Why does the bell setting do nothing?
Because the bell is off in your terminal's own settings. Claude Code emits it; the terminal decides whether to make it visible or audible. Check the profile settings of your terminal app.
Can I get a different sound for permission requests than for finished runs?
Yes, with a hook. The Notification event tells you which kind of notification it is, so your script can branch on that and play a different sound with afplay.
Do notifications work when the terminal is in a background Space?
A macOS banner posted through osascript does, since it is a system notification rather than something drawn by the terminal. A terminal bell in a hidden window is much easier to miss.
Does any of this let me approve without switching windows?
Not on its own. Notifications inform; they do not carry an answer back. To answer in place, something has to be attached to the decision channel itself, which is what the approvals guide is about.
See the request, not a notice about it
Noveriq shows every Claude Code session in the notch and lets you answer from there. Download first; it asks for a key when you are ready.
Get Noveriq for Mac$9.99 once · macOS 15 or later · 30 days, money back