When Claude Code runs on a remote server, a notify-send or osascript hook fires on the server, where there is no desktop to show it. There are two routes that work. The first is a terminal escape sequence: Claude Code's built-in notification, or a hook that returns a terminalSequence, travels back over the SSH connection and your local terminal (Ghostty, Kitty or iTerm2) shows it, but only while you are connected and the terminal is open. The second is a push over the network from the server to your phone, through ntfy, a webhook or Claude Code Notifier, which keeps working when your laptop is shut.
01Why local notification commands do nothing over SSH
A hook is a command that Claude Code runs on the machine where Claude Code is running. On a server reached over SSH, that is the server. notify-send there needs a desktop notification daemon, which Anthropic's hooks guide notes headless servers and SSH sessions do not have. osascript and PowerShell toasts are not there at all.
So the question is not "which notification command", but "how does a signal get from the server to me". There are three ways:
02Route 1: notifications through your SSH terminal
Claude Code's own notification is an escape sequence written to the terminal. Anthropic's terminal guide says it reaches your local machine over SSH, so a remote session can still alert you. Ghostty and Kitty pass it to your desktop's notification centre with no set-up. iTerm2 does once you enable it: Settings > Profiles > Terminal, tick Notification Center Alerts, then under Filter Alerts turn on Send escape sequence-generated alerts.
If your terminal is not one of those, or you want your own text, a hook can return an escape sequence for Claude Code to write. Hooks have no terminal of their own, so you return it in a terminalSequence field. Save this on the server as ~/.claude/hooks/notify-ssh.sh:
#!/bin/bash
body=$(jq -r '.message // "Claude Code needs your attention"')
# OSC 9 for iTerm2, WezTerm and ConEmu; use '\033]777;notify;%s;%s\007' for Ghostty
seq=$(printf '\033]9;%s\007' "$body")
jq -nc --arg seq "$seq" '{terminalSequence: $seq}'
Then add it to ~/.claude/settings.json on the server:
{
"hooks": {
"Notification": [
{
"matcher": "",
"hooks": [
{ "type": "command", "command": "~/.claude/hooks/notify-ssh.sh" }
]
}
]
}
}
Run chmod +x ~/.claude/hooks/notify-ssh.sh and install jq on the server. Anthropic's reference lists the sequences it accepts: OSC 9 (iTerm2, ConEmu, Windows Terminal, WezTerm), OSC 99 (Kitty), OSC 777 (urxvt, Ghostty, Warp) and a bare bell. Claude Code only writes the sequence in an interactive session while its interface is on screen.
The limits are built in. If the SSH connection drops, your laptop sleeps, or you close the tab, nothing arrives. If you run Claude Code in tmux, add set -g allow-passthrough on to ~/.tmux.conf on the server; the tmux guide covers that and detached sessions.
03Route 2: a push from the server
A push does not depend on your terminal, so it is the right choice for long jobs. The hook makes an HTTPS request from the server, and a service delivers it to your phone or chat. The ntfy guide and the Telegram, Slack and Discord guide have working hooks. They tell you something happened. They cannot approve a command or answer a question, so Claude still waits for you to reconnect.
04Route 3: Claude Code Notifier on the server
Claude Code Notifier is an independent tool (made by TouchTap, not by Anthropic). Its free command line tool, ccn, runs on Mac, Windows and Linux, so you install it on the server, where Claude Code runs:
curl -fsSL https://claudecodenotifier.com/install.sh | sh
It adds its hook entries to the server's ~/.claude/settings.json, keeps the rest and makes a backup. ccn's desktop notifications appear on the machine ccn runs on, which on a headless server means nowhere useful. The phone is the useful route: phone notifications go through TouchTap's encrypted relay server, not through your SSH connection, so they work from any machine ccn runs on as long as it can reach the internet.
Pairing over SSH
Pairing needs no browser on the server. Either run ccn pair in your SSH session; it prints a QR code in the terminal that you scan with your phone's camera, and a link valid for 24 hours. Or open the app on your phone, go to Computers > Add a computer, and run the line it shows, which installs and pairs in one step:
curl -fsSL https://claudecodenotifier.com/install.sh | sh -s 482913
(The six-digit code is an example; use the one your app shows.) One phone can have several computers, so a laptop and a server can both report to it. See the pairing guide for details.
What you can do from the phone
Once paired, you get a push when Claude finishes, needs a permission or asks a question. You can allow once, allow for the session or deny a permission, pick an answer to Claude's question, and send the next instruction after Claude finishes. If you are away, the terminal waits for the phone's answer, up to a ceiling you choose, and stops waiting the moment you type at the keyboard. Run ccn doctor on the server to check the hooks, the credentials and the connection to the relay. See pricing for the phone apps; the command line tool is free.
05Keep Claude Code alive when SSH drops
All three routes need Claude Code itself to keep running. If you start claude straight from your SSH shell and the connection drops, the session ends with it. Run it inside tmux or GNU screen so it survives a disconnect, then reattach later. Hooks keep firing in a detached session, which is where a phone push earns its keep. The tmux guide and walking away from long tasks cover this workflow.
06FAQ
Why does notify-send do nothing when I use Claude Code over SSH?
The hook runs on the server, and the server has no desktop notification daemon. Use an escape sequence that your local terminal understands, or a push service.
Does Claude Code's built-in notification work over SSH?
Yes, in terminals that support it. Anthropic's docs say the desktop notification reaches your local machine over SSH; Ghostty and Kitty show it without set-up, and iTerm2 after you enable escape-sequence alerts. It needs the connection to be open.
Do I install ccn on my laptop or on the server?
On the server, because that is where Claude Code and its hooks run. Install it on your laptop too if you also run Claude Code there; one phone can be paired with both.
Will I still get notified if my SSH session disconnects?
Only with a push from the server, and only if Claude Code is still running, for example inside tmux. Escape sequences stop the moment the connection closes.
Does the server need any open ports for phone notifications?
ccn needs no inbound connection. It needs outbound internet access to reach the relay server.