# Cursor push notifications: a ping when the agent finishes

> Get a push on your phone when a Cursor agent finishes, fails or has something to tell you. A stop hook for every run, plus the Pocket Alert MCP server for messages the agent writes itself.

*12.10.2026*

<!-- source: https://pocketalert.app/blog/cursor-push-notifications -->

Cursor's agent can refactor a module while you make coffee, but Cursor only tells you it is done on the computer: a sound, or a system notification if you turned them on. Cloud Agents used to push a "finished" notification to your phone too; since the end of August 2026 they only do that when the agent asks a question or needs approval.

If you want a push on your phone when a local agent run ends, Cursor gives you two ways to send one:

- **A `stop` hook.** Cursor runs your script every time the agent loop ends, whether it completed, was aborted or hit an error. Reliable, the same every time.
- **An MCP tool.** Add the Pocket Alert MCP server and the agent can send you a push whenever you ask it to, with a message it writes.

This guide sets up both. Each takes a few minutes.

## What you need

- Cursor 1.7 or later; hooks arrived in 1.7.
- `jq` and `curl` for the hook.
- A Pocket Alert account, an API key from [API Keys](/account/apikeys), and the app on your phone. The Free plan is enough.

Cursor launched from the Dock does not read your `.zshrc`, so environment variables you export there are invisible to it. Keep the key in a file instead:

```bash
mkdir -p ~/.config/pocketalert
printf '%s' 'YOUR_API_KEY' > ~/.config/pocketalert/key
chmod 600 ~/.config/pocketalert/key
```

## Option 1. A stop hook for every run

### The script

Cursor passes the hook a JSON object on stdin. For `stop` it includes `status` (`completed`, `aborted` or `error`) and `workspace_roots`, the folders open in the window. Save this as `~/.cursor/hooks/pocketalert.sh`:

```bash
#!/usr/bin/env bash
payload=$(cat)
project=$(basename "$(jq -r '.workspace_roots[0] // "cursor"' <<<"$payload")")
status=$(jq -r '.status // "completed"' <<<"$payload")

case "$status" in
  completed) title="Cursor finished · $project"; level=default ;;
  error)     title="Cursor hit an error · $project"; level=high ;;
  *)         echo '{}'; exit 0 ;;
esac

jq -n --arg t "$title" --arg m "Agent run $status" --arg l "$level" \
  '{title: $t, message: $m, level: $l}' |
  curl -sS --max-time 5 https://api.pocketalert.app/v1/messages \
    -H "Token: $(cat ~/.config/pocketalert/key)" \
    -H 'Content-Type: application/json' \
    --data-binary @- >/dev/null 2>&1 || true

echo '{}'
```

```bash
chmod +x ~/.cursor/hooks/pocketalert.sh
```

Aborted runs are skipped on purpose: if you pressed stop, you already know. The script prints `{}` because a `stop` hook can return a `followup_message` that Cursor sends back to the agent as your next prompt; an empty object means "nothing to add".

### Or with the Pocket Alert CLI

With the [Pocket Alert CLI](/tools/cli) 2.0 or later installed, the curl block shrinks to one command. The CLI escapes the text itself and takes the key from `pocketalert auth`:

```bash
pocketalert send -t "$title" -m "Agent run $status" -l "$level" -q >/dev/null 2>&1 || true
```

Keep `-t` and `-m` rather than positional arguments: a reply that starts with a dash, like a Markdown list, would otherwise be read as an option.

### Register it

Create `~/.cursor/hooks.json`:

```json
{
  "version": 1,
  "hooks": {
    "stop": [
      { "command": "/Users/you/.cursor/hooks/pocketalert.sh", "timeout": 10 }
    ]
  }
}
```

Use the absolute path to the script; replace `/Users/you` with your home directory. The file in your home folder applies to every project. For one project only, put it in `.cursor/hooks.json` in the repository.

Restart Cursor, give the agent a small task, and your phone shows **Cursor finished · your-project** when it ends.

### Want the agent's answer in the push?

`stop` does not include the agent's reply, but `afterAgentResponse` does, as `text`. To push the first lines of the answer instead of just the status, register the same kind of script on `afterAgentResponse` and read `.text` from stdin. It fires after every response, so it is noisier; most people keep `stop`.

## Option 2. The MCP server

The hook tells you that a run ended. The MCP server lets the agent tell you what happened. Add Pocket Alert to `~/.cursor/mcp.json`:

```json
{
  "mcpServers": {
    "pocketalert": {
      "url": "https://mcp.pocketalert.app/mcp",
      "headers": { "Authorization": "Bearer YOUR_API_KEY" }
    }
  }
}
```

The key is written into the file because of the Dock issue above. This file lives in your home folder, not in a repository. If you start Cursor from a terminal with the key exported, `"Bearer ${env:POCKETALERT_API_KEY}"` works instead.

Open the MCP section of Cursor Settings, check that `pocketalert` shows its tools, and ask:

```text
> Run the test suite. When it's done, push me the number of failures with Pocket Alert.
```

The agent calls `create_message` with a summary it wrote. It can also set `level`, so a failing suite can arrive as `high`, and attach action buttons on a paid plan.

To make it a habit rather than a per-prompt instruction, add a project rule:

```text
When a task takes more than a few minutes, finish by calling the pocketalert
create_message tool with a one-line summary. Use level "high" if anything failed.
```

## Which one to use

<div class="cmp-table-wrap">
<table class="cmp-table">
<thead><tr><th></th><th>Stop hook</th><th>MCP tool</th></tr></thead>
<tbody>
<tr><th>Fires</th><td>Every run, always</td><td>When the agent decides to</td></tr>
<tr><th>Message</th><td>Status: completed or error</td><td>Whatever the agent writes</td></tr>
<tr><th>Needs</th><td>A script and <code>hooks.json</code></td><td>One block in <code>mcp.json</code></td></tr>
<tr><th>Can fail silently</th><td>Only if the network is down</td><td>If the model forgets</td></tr>
</tbody>
</table>
</div>

Both together is a good default: the hook guarantees you hear about every run, the MCP tool gives you the content when it matters.

## Approvals

Cursor has no hook for "the agent is waiting for approval". `beforeShellExecution` and `preToolUse` fire before every command, including the ones Cursor runs without asking, so they cannot tell you when you are actually needed. Cursor's own notifications and Cloud Agents' phone pushes cover that case today.

If you want the agent to stop and ask before a risky step, give it the MCP tool and a rule to send a push with Approve and Decline buttons; [Human-in-the-loop notifications](/blog/human-in-the-loop-notifications) shows how to make that a real gate rather than a polite request.

## If nothing arrives

- **Run the script by hand:** `echo '{"status":"completed","workspace_roots":["/tmp/demo"]}' | ~/.cursor/hooks/pocketalert.sh`. If no push arrives, remove `>/dev/null 2>&1` to see curl's error.
- **Restart Cursor** after editing `hooks.json`; hooks are read when it starts.
- **Check the path** in `hooks.json` is absolute and the script is executable.
- **For MCP, check the tools list.** An error next to the server in Cursor's MCP settings usually means the key is wrong or empty.

## FAQ

### Does Cursor have push notifications built in?

On the computer, yes: a completion sound and system notifications. On the phone, only Cloud Agents push, and since late August 2026 only for questions and approvals, not for finished turns. The setup here covers local agent runs.

### Will this work for background and cloud agents?

The `stop` hook runs on your machine, so it covers agents running locally. Cloud Agents run on Cursor's servers; for those, the MCP route works if the agent has the server configured, or use Cursor's Slack integration.

### Does the agent see my API key?

In the hook setup, no: the key stays in a file only the script reads. In the MCP setup, the key sits in Cursor's config and is sent to the MCP server, not to the model.

### Is it free?

Yes. Hooks and MCP are part of Cursor, and Pocket Alert's Free plan covers 50 messages a day on one device.

[Get an API key](/signup), add the hook, and let Cursor tell you when it's done.
