Add Push Notifications to a Cline or Aider Session
Cline gets a notification tool through MCP. Aider gets a shell wrapper. Two setups, same result: your phone tells you when the agent has stopped.

Terminal coding agents have a particular way of wasting your time. They are autonomous enough that you walk away, and silent enough that walking away costs you twenty minutes every time.
Cline and Aider are the two that come up most, and they need different fixes. Cline speaks MCP, so it gets a tool it can call. Aider does not, so it gets a wrapper around the process. Both take about five minutes, and both end with your phone buzzing instead of you tabbing back.
First, the app key
Install TheNotificationApp on your iPhone, sign in with Apple, and create an application. Call it Coding agents. Copy the app_key. Both halves of this post use it, though Cline will end up not needing it directly.
Cline: give it a tool
Cline supports MCP servers, which means you can hand it a notification capability the same way you hand it a filesystem or a database. Open the MCP settings in the Cline panel and add:
{
"mcpServers": {
"notifications": {
"url": "https://thenotification.app/api/mcp"
}
}
}The server is hosted and streams over HTTP, so there is no local process, no npx command, and nothing to keep running. On first use you authorise it with Sign in with Apple rather than pasting a key, which means nothing long-lived ends up in a settings file that syncs across your machines.
Then tell it when to use the tool. The reliable place is .clinerules in the repo, so it applies to every task rather than the one prompt you remembered to write it in:
# Notifications
Send a push notification when you finish a task that took more than two
minutes, and when you stop because you need a decision from me.
Put the outcome in the body: how many files changed and whether the tests
passed. Do not notify on individual file edits.That last line does the heavy lifting. An agent with a notification tool and no rules will use it, and a phone that buzzes per file edit gets muted by lunchtime.
Aider: wrap the process
Aider has no hook that fires when a session ends, so there is nothing to register. What it does have is an exit code, which is enough.
Put this in your shell config:
export TNA_APP_KEY="your_app_key_here"
notify() {
curl -fsS -X POST https://thenotification.app/api/sendNotification \
-H "app_key: $TNA_APP_KEY" \
-H "Content-Type: application/json" \
-d "$(jq -n --arg t "$1" --arg b "$2" '{title: $t, body: $b}')" \
>/dev/null
}
ai() {
local repo start elapsed status changed
repo="$(basename "$PWD")"
start="$SECONDS"
aider "$@"
status="$?"
elapsed="$(( SECONDS - start ))"
changed="$(git diff --shortstat HEAD 2>/dev/null | xargs)"
[ -z "$changed" ] && changed="no changes"
if [ "$status" -eq 0 ]; then
notify "Aider done in ${elapsed}s ($repo)" "$changed"
else
notify "Aider exited $status ($repo)" "$changed"
fi
}Run ai instead of aider and you get a notification when the session ends, with the repo, the duration, and what actually changed on disk.
Two details worth the extra lines. Building the JSON with jq -n rather than string interpolation means a branch name with a quote in it cannot break the request, which is the kind of bug that only shows up on the day you need the alert. And git diff --shortstat is what turns "Aider done" into "3 files changed, 87 insertions(+), 12 deletions(-)", which is the difference between an alert you act on and one you have to investigate.
Make the failure case louder
A non-zero exit from Aider usually means you stopped it or it hit something it could not handle. Either way that is the notification you actually care about, so give it a title that reads differently at a glance:
if [ "$status" -eq 0 ]; then
notify "Aider done in ${elapsed}s ($repo)" "$changed"
elif [ "$status" -eq 130 ]; then
: # you pressed Ctrl-C, you already know
else
notify "Aider needs you ($repo)" "exit $status after ${elapsed}s. $changed"
fiSkipping exit code 130 matters more than it sounds. That is Ctrl-C, and getting a push notification about a thing you just did by hand is exactly the sort of noise that trains you to ignore the channel.
Adding a tap-through
Both setups take an optional link, which makes the notification open something when tapped. For a coding agent the useful target is the diff:
branch="$(git rev-parse --abbrev-ref HEAD)"
curl -fsS -X POST https://thenotification.app/api/sendNotification \
-H "app_key: $TNA_APP_KEY" \
-H "Content-Type: application/json" \
-d "$(jq -n --arg t "Aider done ($repo)" --arg b "$changed" \
--arg l "https://github.com/you/$repo/compare/$branch" \
'{title: $t, body: $b, link: $l}')" >/dev/nullThe full field list is in the API reference, and the shell-specific patterns are in sending push notifications from a Bash script.
Which approach is better
The MCP tool is nicer when it works, because the agent writes the message and it knows what it did. The wrapper is more reliable, because a shell exit code cannot forget to fire.
In practice, if your agent supports MCP use the tool, and if it does not, wrap it. If you find yourself with a model that keeps forgetting to call the tool, wrap that too and accept a slightly duller notification in exchange for one that always arrives.
The honest part
The free tier is 100 notifications total, for the lifetime of the account, not per month. One per session is comfortable: a heavy week of agent work might be forty. Pro is $2.99 a month for 1,000 if you outgrow it.
This is iPhone only, because it rides Apple's push service. And the wrapper needs jq, which is one brew install jq away but is not on a fresh machine by default.
Wire whichever half applies to you, then start the next long task and actually leave the desk. If you also run Claude Code, the same idea through its Stop hook is in Claude Code push notifications.
New to this? Start with what an MCP server is.



