notify-mcp
by slchenchn
README.md
# page-user — proactive push-notification MCP
English | [简体中文](README.zh-CN.md)
An MCP server that gives any MCP client (Claude Code, Codex CLI, …) a `page_user`
tool. The AI calls it to proactively push a short message to your phone via
Telegram — when a long task finishes, a run fails, it needs a decision, or you
asked for periodic progress. Each message carries the **machine name**, **which
CLI** sent it, a **session label**, and optional **tables** (rendered aligned).
```
Claude Code / Codex ── page_user(message, title?, host?, session?, agent?, table?, tag?) ──►
(just a URL + token in config) │
▼
Cloudflare Worker (remote MCP) ──► Telegram
```
There are two flavors. The **Cloudflare remote MCP** is recommended; the original
**self-hosted Apprise gateway** is kept as a legacy alternative (see the end).
## Recommended: remote MCP on Cloudflare
The whole server is one Cloudflare Worker in [`cloudflare/`](./cloudflare) —
stateless MCP over Streamable HTTP, bearer auth, pushes straight to Telegram.
Nothing runs on your machines; you update every client at once by redeploying.
1. **Deploy** the Worker and set its secrets — full step-by-step in
[`cloudflare/README.md`](./cloudflare/README.md). You end up with an endpoint
like `https://<name>.<subdomain>.workers.dev/mcp` and a bearer `AUTH_TOKEN`.
2. **Register** with your CLIs on each machine:
```bash
PAGE_USER_TOKEN=<AUTH_TOKEN> ./scripts/install_page_user.sh
```
This removes any old `notify` server and adds `page-user` to Claude Code (user
scope) and Codex (`~/.codex/config.toml`), baking in the machine's hostname
(`X-Host`) and the CLI name (`X-Agent`). Override the endpoint with
`PAGE_USER_URL` if you deployed under a different name.
3. **Verify**: `claude mcp list` (should show `page-user … ✔ Connected`) and
`codex mcp list`.
> **Egress:** clients must be able to reach `*.workers.dev` (the default Worker
> domain is commonly blocked on direct connections — a working HTTP(S) proxy in
> the environment is enough; the CLIs inherit `HTTPS_PROXY`). If a machine can't
> reach `*.workers.dev` at all, bind a custom domain to the Worker instead.
### The tool
`page_user(message, title?, host?, session?, agent?, table?, tag?)` — pushes a
formatted notification. Only `message` is required; `host`/`agent` default to the
`X-Host`/`X-Agent` headers set at registration, `tag` selects a recipient group
(`TARGETS` secret), and `table` is rows of strings rendered as an aligned
monospace table. The model learns all of this from the tool's MCP schema — it
does not read this README. To change the tool or its guidance, edit
`cloudflare/worker.js` and `npx wrangler deploy`; all clients pick it up.
### Manual registration
```bash
# Claude Code (user scope):
claude mcp add --transport http page-user https://<name>.<subdomain>.workers.dev/mcp \
-H "Authorization: Bearer <AUTH_TOKEN>" -H "X-Host: $(hostname)" -H "X-Agent: claude-code" -s user
# Codex — add to ~/.codex/config.toml:
# [mcp_servers.page-user]
# url = "https://<name>.<subdomain>.workers.dev/mcp"
# http_headers = { "Authorization" = "Bearer <AUTH_TOKEN>", "X-Host" = "<host>", "X-Agent" = "codex" }
```
---
## Legacy: self-hosted Apprise gateway
The original design is a thin Python MCP wrapper (`notify_mcp.py`, tool
`send_notification`) that POSTs to a self-hosted Apprise gateway (`server.py`),
which forwards to Telegram/Feishu/etc. Use this if you can't or don't want to use
Cloudflare. It is managed with [uv](https://docs.astral.sh/uv/):
```bash
uv sync --extra gateway # wrapper + apprise
./scripts/start_gateway.sh # run the gateway (needs ./token and ./targets.json)
./scripts/install_mcp.sh # register the `notify` MCP with Claude Code + Codex
```
Details:
- [`docs/NOTIFY_GATEWAY.md`](./docs/NOTIFY_GATEWAY.md) — example gateway deployment (systemd, proxy, Telegram/Feishu).
- [`docs/MCP_INTROSPECTION.md`](./docs/MCP_INTROSPECTION.md) — how a client discovers the server's identity and tool.
- [`docs/ABOUT.txt`](./docs/ABOUT.txt) — short, human-friendly intro (中文).
## Unit tests (legacy Python code)
```bash
uv run pytest -q # no network needed
uv run pre-commit run --all-files # ruff format/lint
```
TDQS
A4.5/5.0
Scored across 1 tool
Disambiguation5/5
Only one tool exists, so there is no possibility of confusion between tools. The purpose is clearly distinct by default.
Naming Consistency5/5
With a single tool, naming is trivially consistent. The name 'send_notification' follows a clear verb_noun pattern.
Tool Count3/5
One tool is borderline appropriate. The server's scope is limited to sending notifications, which could justify a single tool, but it feels thin compared to typical MCP servers that offer multiple operations.
Completeness2/5
The tool only sends notifications; there are no operations for managing recipients, viewing history, or configuring channels. While the core action is covered, significant gaps exist for a complete notification system.
Maintenance
ActivityInactive
ResponsivenessNo issues