Alerts
Sends alert notifications to a person through Discord as one of the Apprise-supported delivery channels.
Delivers alerts by publishing to an ntfy topic that the ntfy phone app subscribes to, causing a push notification on the recipient's phone with no account needed.
Delivers alerts to a Slack channel via an Incoming Webhook URL (e.g. a Slack app posting into its own DM), so agent notifications arrive as a Slack message; replies must go through the link the alert carries.
Sends alert notifications to a person through Telegram as one of the Apprise-supported delivery channels.
This server does not currently have a deployable release on Glama. Follow the repository's setup instructions to install and configure it for your MCP client, including any required credentials and local dependencies.
alerts
An MCP server that lets an AI agent push an alert to a person's phone, with a
link back to the conversation so the person can reply there. A long task
finished, a decision is needed, something failed: the agent calls notify,
the person's phone buzzes, a tap opens the conversation. Built on
mcp-app: the same binary runs locally
over stdio for one person or as a hosted multi-user service over HTTP, where
users sign in with the operator's identity provider.
Delivery is by Apprise, so a channel can be a Slack webhook, an ntfy topic, Pushover, Telegram, Discord, email and more. Every channel is one-way: a reply typed in the notification app never reaches the agent, and the link the alert carries is how the person gets back.
Install
pipx install git+https://github.com/echomodel/alerts.gitCommand | What it does |
| The MCP server: |
| Operator commands: |
Python 3.11 or newer.
Related MCP server: hitl-mcp
Configure channels
A channel is a name an agent sends to (phone, work). Behind each name
is one or more Apprise destination URLs, each carrying that service's
credential (the webhook, the topic, the SMTP login). Those URLs are the only
secret this server has, and they live in one of two places:
the file
~/.config/alerts/channels($XDG_CONFIG_HOME/alerts/channels), for a local server;the
ALERTS_CHANNELSenvironment variable, for a container, where the platform injects it from its secret store.
The variable wins when both exist. The content is the same: one destination
per line, name = url.
# ~/.config/alerts/channels (chmod 600)
phone = https://hooks.slack.com/services/T.../B.../...
phone = ntfys://ntfy.sh/some-long-random-topic
work = mailtos://user:password@smtp.example.com:587/?to=me@example.comA name on several lines sends to every one of them; one line may carry
several comma-separated names. Lines Apprise rejects are reported by name in
list_channels (to an administrator), never by URL. The URL never appears in
a tool response, a log line, or the per-user store.
Setup guides, with every step and gotcha: Slack webhook
(a Slack app's Incoming Webhook posting into its own DM, so Slack pushes it like
a message from someone else) and ntfy (a topic the
ntfy phone app subscribes to; its own app, no account needed). The
list_channel_types tool lists every documented type with its URL form and
Apprise's documentation link.
Run locally (stdio)
claude mcp add --scope user alerts -- alerts-mcp stdio --user local
agy mcp add alerts -- alerts-mcp stdio --user localThe stdio user is the operator: every configured channel is available to
them without a grant. With one channel configured it is the default; with
several, pick one once through the grant_channel tool:
grant_channel(email="local", channel="phone", default=true)Then, from the agent:
notify(message="The import finished: 1,204 rows, 3 rejected. Shall I retry the rejects?",
link="https://claude.ai/chat/...", urgency="normal")Smoke test from a shell, without a client: start the server and send a
tools/call over stdio, or run make test, which covers the transport in
process.
Tools
Tool | Who | What |
| any user | Send an alert to a channel (the user's default when omitted). |
| any user | The channels the caller can send to and their default. Administrators also see every configured channel and any invalid line. |
| any user | The documented destination types, their URL form, notes, and Apprise's docs link. |
| administrators | Let a user send to a channel; optionally make it their default. |
| administrators | Take a channel away from a user. |
The tool descriptions carry the full guidance an agent needs (when to alert,
what to put in the message, always pass the link). Refusals come back as
{"error": ...} naming the channels the caller may use.
Over HTTP a user may send only to channels an administrator granted them;
a user's first granted channel becomes their default. Over stdio the local
operator has every channel. mcp-app's own admin tools (users_list,
user_register, user_block, usage_report) are present too, visible to
administrators only.
Deploy (HTTP)
The server is a standard Python web service. Any environment that can run the container, set the variables in Environment variables, and keep one directory across restarts can host it. This repo carries no deployment-tool configuration: where and how a deployment runs belongs to the operator's own deployment records.
Start command:
alerts-mcp serve, listening on0.0.0.0:8080(--host,--portto change).PORTis not read.MCP endpoint:
/(root). Health:GET /health, no auth. Discovery:GET /.well-known/oauth-protected-resource, no auth.Auth: standard MCP OAuth. The server validates the identity provider's tokens itself; the platform must let unauthenticated traffic through to the app and forward
Hostunchanged.Storage: one directory (
APP_USERS_PATH) that survives restarts.Secrets: only
ALERTS_CHANNELS. Inject it from the platform's secret store, never from the repo or an image.
Docker
docker build -t alerts .
docker run -p 8080:8080 \
-e AUTH_ISSUER=https://login.example.com/ \
-e ADMIN_EMAILS=you@example.com \
-e ALERTS_CHANNELS="$(cat ~/.config/alerts/channels)" \
-v /persistent/path:/data -e APP_USERS_PATH=/data/users \
alertsAny container platform (Cloud Run, Fly, Render, Kubernetes, a VM with systemd) takes the same image and the same variables, set its own way.
Identity provider
Once per deployment, at the identity provider:
An API (resource) whose identifier is the server's public URL, exactly as users will type it, trailing slash included; refresh tokens allowed.
The sign-in method users should see (Sign in with Google is a common choice).
Client identification from the server URL alone: client ID metadata documents where supported, else dynamic client registration.
The user's email in access tokens; if the issuer namespaces custom claims, set
AUTH_EMAIL_CLAIMto the full claim name.Default user-delegated access for third-party clients, if the issuer treats dynamically registered clients as third party.
An MCP client then needs only the URL: it discovers the issuer, signs the user in, and holds the token. Nothing is pasted anywhere.
Environment variables
Every variable the server reads. The table is for scanning; the reference below it has the format, an example, and what happens when a value is missing or wrong. All are read once, at startup: a change needs a restart.
Variable | Applies to | Required | Type | Default when unset | Secret |
| HTTP | yes | URL | none: startup fails | no |
| HTTP | yes, to administer | list of emails | none: no administrators | no |
| both | yes, unless the channels file exists | multi-line text | the channels file | yes |
| both | yes, for durable data | directory path |
| no |
| both | recommended | list of filesystem types | no check | no |
| HTTP | if the issuer namespaces it | claim name |
| no |
| HTTP | no | enum: |
| no |
| HTTP | no | integer ≥ 1 | unlimited | no |
| HTTP | no | URL | derived from the request's | no |
| HTTP | no | URL | OpenID discovery from the issuer | no |
| HTTP | no | list of client ids | none | no |
| HTTP | no | integer, seconds |
| no |
| both | no | directory path |
| no |
| both | no | directory path |
| no |
Lists are comma-separated; spaces around items are ignored.
Variable reference
AUTH_ISSUER — the identity provider that issues access tokens.
Format: the issuer URL exactly as in the token's
issclaim, trailing slash included if the issuer uses one. Example:https://login.example.com/.Missing:
alerts-mcp serverefuses to start, naming the variable. stdio ignores it.Wrong: every request after sign-in gets
401. The startup linemcp_app.startup auth issuer=...shows the value in use; a missing or extra trailing slash is the usual mistake.
ADMIN_EMAILS — who administers this deployment.
Format: comma-separated emails, compared lower-cased. Example:
you@example.com,ops@example.com.Effect: each email is admitted on first sign-in, sees the admin tools (
grant_channel,revoke_channel,users_list,user_register,user_block,usage_report), and may call/admin.Missing: the server serves registered users but nobody can administer it or bootstrap the first user; startup logs a warning. Changing the list is a restart.
ALERTS_CHANNELS — the channel definitions, the server's only secret.
Format: one destination per line,
name = apprise-url; several comma-separated names may share a line, and a name on several lines sends to all of them. Blank lines and#comments are ignored. See Configure channels.Precedence: when set and non-empty it replaces the channels file (
$XDG_CONFIG_HOME/alerts/channels) entirely.Missing (and no file): every tool answers that no channels are configured; nothing fails at startup.
Wrong: a line Apprise rejects is skipped and reported to administrators by channel name and line number in
list_channels, never by URL.Handling: inject from a secret store; it carries every destination's credential. Rotate by replacing a line and restarting.
APP_USERS_PATH — where per-user records (admission state, grants)
and usage counters live.
Format: a directory path; created if missing.
Default:
$XDG_DATA_HOME/alerts/users, which in a container is the image's filesystem and is lost on restart. Point it at a mounted volume for any real deployment.
REQUIRED_FS_TYPE — a startup guard that the data directory is on the
storage you meant.
Format: comma-separated filesystem types, matched on
.boundaries (fusematchesfuse.gcsfuse). Examples:fuse,nfs,ext4,xfs.Effect: if
APP_USERS_PATHis missing, unwritable, or on another filesystem type, the process exits non-zero instead of serving with data that would vanish. Unset: no check;/healththen reports storageunverifiedand statusdegraded.
AUTH_EMAIL_CLAIM — the access-token claim carrying the user's email.
Format: a claim name. Issuers that refuse standard claim names in access tokens need the full namespaced name, e.g.
https://example.com/email.Wrong: signed-in users get
401(no email in the token).
AUTH_ADMISSION — who besides ADMIN_EMAILS gets in on first sign-in.
Values:
allowlist(default): only emails an administrator registered withuser_register; anyone else is parkedpendingwith a403.open: any email the issuer vouches for is approved, up toMAX_USERS.Note: open admission still gives a new user no channels; an administrator grants them with
grant_channel.
MAX_USERS — cap on user records sign-in may create.
Format: a positive integer. Unset: unlimited. Anything else: startup fails naming the value. Administrators are never counted out.
AUTH_AUDIENCE — pins the audience a token must carry.
Default: the URL the request arrived at, from
Host. Set it only when the server is reached by IP address or under several hostnames.
AUTH_JWKS_URI — where the issuer's signing keys are.
Default: found by OpenID discovery from
AUTH_ISSUER. Set it only for an issuer without discovery.
ADMIN_CLIENT_IDS — machine clients treated as administrators.
Format: comma-separated client ids from client-credentials tokens. For scripts that call the admin tools; the principal is
client:<id>.
USAGE_FLUSH_SECONDS — how often tool-call counters reach the data
directory.
Format: integer seconds. Default
60. stdio flushes after every call.
XDG_CONFIG_HOME, XDG_DATA_HOME — the standard base directories.
They place the channels file (
$XDG_CONFIG_HOME/alerts/channels), the admin CLI's saved target ($XDG_CONFIG_HOME/alerts/setup.json), and the default data directory. Rarely set by hand.
Not read: PORT (use --port) and any log-level variable.
Connect the admin CLI
After a deploy, point the admin CLI at it. connect takes the URL and
nothing else: the server holds no secret and the identity provider does the
sign-in. The target is saved in ~/.config/alerts/setup.json, so later
commands take no URL. Deploying never connects the CLI; do it explicitly.
alerts-admin connect https://alerts.example.com
alerts-admin probe
alerts-admin register
alerts-admin connect local # switch to the local store; the remote stays saved
alerts-admin connect remote # and backAgainst a deployment, health, probe and register work without signing
in. users, tools and safe-tool need a signed-in administrator, which
the CLI cannot do yet; against a remote they exit saying so. Users on a
deployment are managed through the admin MCP tools below, from an
administrator's own MCP client.
Manage users and grants
Who gets in: every ADMIN_EMAILS entry on first sign-in, and every user an
administrator registered. Anyone else who signs in is parked pending with a
403 until approved. From an administrator's MCP client:
users_list() # pending first, then approved, blocked
user_register("alice@example.com") # approve, in advance or after a pending sign-in
grant_channel("alice@example.com", "phone") # her first channel is also her default
grant_channel("alice@example.com", "work", default=true)
revoke_channel("alice@example.com", "work")
user_block("bob@example.com") # refuse from now on; data staysA registered user with no grant can sign in and list channels, and notify
tells them to ask an administrator. The administrator's own first use is the
same two steps: sign in, then grant_channel for their own email.
Rotating a channel credential
A credential lives only in the channel definitions. Replace the line
(~/.config/alerts/channels locally; the platform's secret store and a
restart for a deployment). Grants refer to
the channel by name, so nothing else changes. When a webhook or topic leaks,
revoke it at the service first, then replace the line.
Locally, alerts-admin connect local then alerts-admin users list shows
the local store; grants for the local user are set through grant_channel
over stdio.
Sanity-check the deployment
alerts-admin probereports Health: healthy (or degraded) and OAuth: ok with the issuer
and the resource URL. The issuer must be the identity provider you set;
the resource must be the URL users type (a different scheme or host means
the front door rewrites Host; fix the proxy or pin AUTH_AUDIENCE). Then
the end-to-end proof: add the URL in your own MCP client, sign in, call
list_channels, grant yourself a channel, and notify yourself with a
link. The phone should buzz and the tap should land in the conversation.
Connect end users
alerts-admin registerprints the registration command for each client. No command carries a credential, so the same output serves every user:
claude mcp add --transport http -s user alerts https://alerts.example.com/
agy mcp add alerts https://alerts.example.com/
# Claude.ai and Claude mobile: Settings > Connectors > Add custom connector
https://alerts.example.com/Each client signs the user in with the identity provider on first use. A
user whose email is not registered gets a 403 until an administrator runs
user_register; then grant_channel gives them somewhere to send.
Usage report
An administrator asks the connector for usage_report(week="current") or
"prior": users (total, new, active), tool calls (total, errors, per tool)
and the most active users with masked emails. Non-administrators never see
the tool.
Files
Path | Purpose |
| Channel definitions for a local server; |
| Per-user store: admission record and |
| The admin CLI's saved |
Variables are in Environment variables.
urgency maps to Apprise's notification type: low to info, normal to
warning, high to failure. Services that colour or tag by type show it
(Slack: blue, orange, red); ntfy does not. A link is appended to the
message body as a plain URL on its own line, which phones render as a
tappable link.
Further reading
Slack webhook channel: create the Slack app, turn on Incoming Webhooks, aim it at the app's own DM, and the phone notification settings that make it buzz.
ntfy channel: pick a topic, subscribe the phone app, self-host or use ntfy.sh, and what the priority settings do.
CONTRIBUTING.md: architecture, the rules that keep credentials out of responses, tests, releases.
For agents: optional skill accelerant
If you are a coding agent that supports skills, the mcp-app skill provides step-by-step workflows for authoring and operating apps on this framework. It is optional; this README is self-sufficient.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
This server cannot be deployed
Maintenance
Related MCP Connectors
Let your AI agents ask you for decisions and get your answers back, from your phone or the web.
Send yourself a push notification from an AI assistant. No account, one environment variable.
1Reach your own phone from an AI agent: notifications, approval questions, reminders, ring, files.
Push notifications for AI agents - send instant iPhone notifications from any MCP client.
Related MCP Servers
- AlicenseAqualityDmaintenanceEnables bidirectional communication between AI agents and users through ntfy.sh push notifications, allowing agents to send messages and wait for user responses in asynchronous chat workflows.319 npm1MIT
- AlicenseAqualityCmaintenanceEnables AI agents to request human decisions and send notifications through chat platforms like Slack and WhatsApp, with ephemeral in-memory pending requests and no central backend.225 npmMIT
- AlicenseNot gradedqualityCmaintenanceEnables AI agents to exchange direct messages and post to channels, check inboxes with optional blocking waits, page conversation history with automatic size-capped summaries, and attach or retrieve files. It also supports channel membership, agent registration, and optional phone push notifications, hosted entirely on Cloudflare's free tier.3,256 npm1MIT

tmgr-notifyofficial
AlicenseBqualityBmaintenanceEnables AI agents to reach users on their phone through push notifications, and to raise urgent, escalating alarms that fall back to a voice call until someone acknowledges. It also ships hooks that notify users automatically when Claude Code or Codex needs input or finishes a turn.36 npmMIT