notify-mcp
by Jimgitsit
README.md
# notify-mcp
Generic push-to-phone notifications for any agent or script. Exposes a `send_notification` **MCP tool** (available in every Claude Code session) plus a **CLI** (`notify`) for cron jobs, daemons, and other non-agent callers — both backed by one provider-agnostic core.
**Why it exists:** Claude Code's built-in `PushNotification` tool suppresses the mobile push whenever the terminal "has focus" or the user typed recently. On a remote setup the terminal always reads as focused, so every push is silently dropped. notify-mcp is an independent path that ignores that gate.
## Architecture
```
caller ─┬─ MCP tool (Claude agents, any session) ─┐
└─ CLI / import (cron, daemons, scripts) ─┴─► sendNotification() ──► provider (ntfy | pushover | telegram) ──► phone
```
- `src/notify.ts` — provider-agnostic core: defaults, truncation, retries w/ backoff, logging. Never throws.
- `src/providers/` — one file per provider behind a common `Provider` interface.
- `src/mcp.ts` — stdio MCP server exposing `send_notification`.
- `src/cli.ts` — thin CLI wrapper.
## For agents (MCP tool)
Tool: **`send_notification`**. Registered globally, so every Claude session/project inherits it.
| Param | Required | Notes |
|---|---|---|
| `message` | yes | The alert body. Lead with the actionable fact. |
| `title` | no | Bold title; defaults to config `defaults.title`. |
| `priority` | no | `min` \| `low` \| `default` \| `high` \| `max`. `high`/`max` can pierce Do-Not-Disturb. |
| `tags` | no | Labels / ntfy emoji shortcodes, e.g. `["warning"]`. |
| `source` | no | Caller name; rendered as `[source]` prefix on the title so the user knows the origin. |
| `click` | no | URL to open when the notification is tapped. |
**When to use it:** genuine *decision + time-sensitivity* — an approval/auth needed, a failure, a long job finished — assuming the user is away. Not routine status updates.
## For scripts (CLI)
```sh
bun run /Users/doug/dev-local/notify-mcp/src/cli.ts \
--title "Deploy" --priority high --source "ci" --tags "rocket" \
"prod deploy finished in 4m12s"
```
Options: `--title/-t`, `--priority/-p`, `--tags <a,b,c>`, `--source/-s`, `--click/-c`, `--help/-h`. Exit code is `0` on success, nonzero on final failure — safe to use in a pipeline guard (`notify "…" || true`).
Programmatic use from another Bun/Node project:
```ts
import { sendNotification } from "/Users/doug/dev-local/notify-mcp/src/notify.ts";
const result = await sendNotification({ message: "hi", priority: "high" });
```
## Config
Lives at `~/.config/notify-mcp/config.json` (`chmod 600`, outside the repo). Override the path with `NOTIFY_MCP_CONFIG`. Template: `config.example.json`.
```json
{
"provider": "ntfy",
"defaults": { "title": "Notification", "priority": "default", "maxLength": 512, "retries": 2, "timeoutMs": 5000 },
"ntfy": { "server": "https://ntfy.sh", "topic": "<long-random-topic>" }
}
```
**Switch providers** by changing `provider` and filling the matching block:
- `ntfy` — `{ server, topic }`. On public ntfy.sh the **topic is the password** — keep it long, random, and out of logs/commits.
- `pushover` — `{ token, user }`. Private by design.
- `telegram` — `{ botToken, chatId }`.
## Logs
Every attempt appends a JSONL line to `~/.config/notify-mcp/notify.log` (ts, provider, ok, status, attempts, title, message, error). Log-write failures are swallowed so they can't break a caller.
## Setup (one-time, user)
1. Install the **ntfy** app (iOS/Android) and subscribe to your topic on server `https://ntfy.sh`.
2. Copy `config.example.json` → `~/.config/notify-mcp/config.json`, set the topic, `chmod 600`.
3. The MCP server is registered in `~/.claude.json` (global). **New** Claude sessions pick it up; an already-running session must be restarted to see the tool.
## Test
```sh
bun run typecheck
bun run src/cli.ts --priority high "test push" # expect: Sent via ntfy (HTTP 200, 1 attempt(s)).
```
This server cannot be deployed
Maintenance
ActivityStale
ResponsivenessNo issues