tmgr-notify
OfficialClick on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@tmgr-notifynotify me that the build finished successfully"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
@tmgr/notify
MCP server and Claude Code / Codex hook CLI that lets an AI agent reach you on your phone.
TMGR is a task manager with an iOS app. With @tmgr/notify (command tmgr-notify) an agent (Claude Code, Codex, or any MCP client) gets three tools:
notify_usersends a push notification.alarmrings you for an urgent incident: an alarm in the app, then a voice call if you do not acknowledge it.alarm_statusre-checks an alarm.
The package also ships hooks that notify you automatically when Claude Code needs input or finishes a long turn, and when a Codex turn completes.
Requirements
Node 22 or newer
A TMGR account
The TMGR mobile app, for push notifications and for alarms (AlarmKit)
Related MCP server: BotBell MCP Server
Get a token
In TMGR open Settings → Agent notifications → Create token. The token (tmgrn_...) is shown once, so copy it right away. You can revoke it from the same screen.
Configuration
Configuration is read from environment variables first, then from a fallback file. Environment variables always win.
Variable | Required | Description |
| no | API base URL, default |
| yes | The |
| no | Minimum turn duration in minutes before |
If a variable is not set, tmgr-notify reads it from ~/.config/tmgr-notify/env, one KEY=VALUE per line, # comments allowed:
TMGR_NOTIFY_TOKEN=<TMGR_NOTIFY_TOKEN>The file holds a bearer token, so restrict it: chmod 600 ~/.config/tmgr-notify/env.
Claude Code
MCP server
Register it once at user scope so it is available in every project:
claude mcp add -s user tmgr-notify \
-e TMGR_NOTIFY_TOKEN=<TMGR_NOTIFY_TOKEN> \
-- npx -y @tmgr/notify mcpTo keep the token out of ~/.claude.json, skip the -e flag and rely on the env file above:
claude mcp add -s user tmgr-notify -- npx -y @tmgr/notify mcpOr add it to a project's .mcp.json:
{
"mcpServers": {
"tmgr-notify": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@tmgr/notify", "mcp"]
}
}
}MCP servers belong in .mcp.json or ~/.claude.json, not in settings.json. Use claude mcp list to check the connection.
Hooks
Hooks live in ~/.claude/settings.json (user scope shown, project scope works the same way). Add all three:
{
"hooks": {
"Notification": [
{
"hooks": [
{ "type": "command", "command": "npx -y @tmgr/notify hook notification" }
]
}
],
"UserPromptSubmit": [
{
"hooks": [
{ "type": "command", "command": "npx -y @tmgr/notify hook prompt" }
]
}
],
"Stop": [
{
"hooks": [
{ "type": "command", "command": "npx -y @tmgr/notify hook stop" }
]
}
]
}
}npx adds startup latency to every hook run. For faster hooks install the package globally with npm i -g @tmgr/notify and use tmgr-notify hook ... as the command.
UserPromptSubmit and Stop do not support a matcher. They fire on every prompt and turn, which is what the hook prompt / hook stop pair needs to measure turn duration.
What each hook does:
Notification(needs your input, permission, or idle prompt): sends a high-priority push titledClaude Code · <project> · needs you, with the first line of the message as the body. This event fires for more than permission prompts, so the wording is generic ("needs your attention"). The typesauth_success,elicitation_completeandelicitation_responseare skipped.UserPromptSubmit: records the turn start time in a per-session state file under~/.cache/tmgr-notify/(falls back to the OS temp directory). No network call.Stop: does nothing ifstop_hook_activeis true. Otherwise it reads the recorded turn start; if that is missing it falls back to the last user prompt timestamp in the transcript (best effort). If the turn lasted at leastTMGR_NOTIFY_STOP_MIN_MINUTES(default 5) it sends a normal-priority push titledClaude Code · <project> · done, with the first line of the last assistant message as the body.
<project> is the basename of the working directory.
Every hook subcommand always exits 0 and writes nothing to stdout, so a misconfigured or failing hook never changes the behaviour of Claude Code or Codex. Diagnostics go to stderr only, and the token is never logged.
Codex
Codex's notify hook is user-level only (~/.codex/config.toml); a project's .codex/config.toml cannot set it. Codex runs the program with the event JSON as the final argv argument (not stdin), for the agent-turn-complete event:
notify = ["npx", "-y", "@tmgr/notify", "hook", "codex"]To also let Codex call the tools directly:
[mcp_servers.tmgr-notify]
command = "npx"
args = ["-y", "@tmgr/notify", "mcp"]
tool_timeout_sec = 660
[mcp_servers.tmgr-notify.env]
TMGR_NOTIFY_TOKEN = "<TMGR_NOTIFY_TOKEN>"Codex cancels MCP tool calls after 60 s by default, while alarm waits up to 600 s; tool_timeout_sec = 660 keeps the result. Omit the env table to rely on ~/.config/tmgr-notify/env instead of putting the token in config.toml.
hook codex sends a normal-priority push titled Codex · <project> with the first line of last-assistant-message as the body, only when type is agent-turn-complete.
Alarm setup
notify_user is a soft channel. alarm is for incidents that need you now (production down, data loss, security). A silent push triggers an alarm in the mobile app. If you do not acknowledge it in time, or the app never receives it, TMGR places a voice call where pressing 1 acknowledges. One alarm call is one escalation; the calling agent decides whether to retry. Prefer notify_user for everything else.
To make alarms reliable:
In TMGR Settings, set and verify an alarm phone. Without it the voice-call fallback is unavailable and the alarm is push-only; it ends as
call_unavailableif the app does not acknowledge it.Add the TMGR caller number to your contacts or Favorites so Focus / Do Not Disturb lets the call through.
On iOS enable Repeated Calls (Settings → Focus → Do Not Disturb → Allow Calls From) so a second call within 3 minutes gets through.
Install the mobile app and allow alarms (AlarmKit) when prompted.
The voice call repeats until acknowledged: up to callAttempts calls (default 3, the server clamps it to 1-5). Each attempt rings for about 55 s, with about 20 s between attempts. Answering machines are detected and hung up, so voicemail never counts as an acknowledgement.
Tools reference
notify_user
Parameter | Type | Description |
| string, required | 1-120 characters. |
| string | Up to 1000 characters. |
|
| Default |
| string | Absolute http(s) URL to open on tap. |
Returns the status on success. A rate limit is returned as a tool error with the retry-after seconds.
alarm
Parameter | Type | Description |
| string, required | 1-120 characters, read aloud on the call. |
| string, required | 1-500 characters, read aloud on the call. |
| integer | Seconds to wait for an app acknowledgement before calling. The server clamps it to 15-900. |
| integer | Seconds to wait for the app to receive the alarm before calling. The server clamps it to 10-300. |
| boolean | Voice-call fallback, default |
| integer 1-5 | Voice-call attempts, default 3. The result includes |
| boolean | Default |
| number, max 3600 | Wait cap, default 600. On timeout the last status is returned with |
With waitForResult it long-polls until a final status or maxWaitSeconds, and returns the status, ack channel, call status and alarm id. While waiting, transient network errors, 5xx and 429 responses are retried up to 3 times (1 s, 2 s, 4 s backoff; Retry-After honored, capped at 10 s); 401 and 404 fail at once. If a call times out client-side the alarm keeps running on the server; re-check it with alarm_status.
alarm_status
Parameter | Type | Description |
| string, required | Alarm id returned by |
| integer 0-50 | Long-poll seconds, default 0. |
Use it after a timeout or after waitForResult: false.
Alarm statuses
Non-final: pending, delivered, calling. Final: acknowledged (channel app or call), no_answer, busy, failed, call_unavailable, expired.
Client tool-call timeouts
A blocking wait can be long, so the client's MCP tool-call timeout matters.
Claude Code CLI:
MCP_TOOL_TIMEOUTdefaults to about 28 hours, so the 600 s default wait fits. A per-servertimeoutin.mcp.jsonoverrides it as a hard limit that progress notifications do not extend. A stdio call that sends no response and no progress for 30 minutes is aborted.Claude desktop app and Cowork: reported to cancel tool calls at about 60 s regardless of
MCP_TOOL_TIMEOUT.While waiting, the server sends
notifications/progressafter every poll when the client supplies aprogressToken. For clients with a short hard cap, pass a smallmaxWaitSeconds(for example 45) orwaitForResult: false, then callalarm_status.
A full default escalation (3 attempts) takes about 6 minutes; 5 attempts take about 9 minutes, so with long timeouts pass a larger maxWaitSeconds.
CLI
npx -y @tmgr/notify send --title "Deploy finished" --body "v1.2.3 is live" --priority high [--link https://example.com]Exits non-zero and prints an error: ... line on failure (auth, validation, rate limit, timeout, network).
npx -y @tmgr/notify alarm "prod DB is down" --title "Prod down" [--no-wait] [--ack-timeout 90] [--delivery-timeout 30] [--no-call] [--call-attempts 3]The default title is Alarm. --no-call disables the voice-call fallback. It prints each status change and the final status. The exit code is 0 only when the alarm was acknowledged (with --no-wait, 0 once the alarm is created).
npx -y @tmgr/notify mcp # MCP server over stdio (also the default with no arguments)
npx -y @tmgr/notify hook notification|prompt|stop # Claude Code hooks, payload on stdin
npx -y @tmgr/notify hook codex '<event json>' # Codex notify, payload as last argumentTo test the backend independently of this CLI:
curl -i -X POST https://api.tmgr.dev/api/notifications/push \
-H "Authorization: Bearer <TMGR_NOTIFY_TOKEN>" \
-H "Content-Type: application/json" \
-d '{"title":"Test","body":"Hello from curl","priority":"normal"}'Security
The token is a bearer secret. Keep it out of repositories, prefer the
~/.config/tmgr-notify/envfile (mode 600), and revoke it in TMGR if it leaks.Agents cannot acknowledge alarms. Only you can, in the app or by pressing
1on the call.Hooks and tools never print the token.
See SECURITY.md for how to report a vulnerability.
Troubleshooting
No push arrives and no error is shown: hooks swallow all errors to stderr and always exit
0by design. Check that your env vars or~/.config/tmgr-notify/envare visible to the shell that Claude Code or Codex spawns (a login shell profile may not be sourced). Run the matchinghooksubcommand by hand with sample stdin or argv to see stderr.hook stopnever fires: short turns stay silent belowTMGR_NOTIFY_STOP_MIN_MINUTES(default 5). Also confirm theUserPromptSubmithook is configured; without ithook stoponly has the transcript fallback, which can be inaccurate.401 from the API: the token was revoked, or another auth header was sent alongside it (the API allows exactly one auth method).
429 from the API: rate limited per user, 20 per minute by default; retry after the
Retry-Aftervalue.MCP tool not showing up in Claude Code: check
claude mcp list, and remembermcpServersdoes not go insettings.json.Codex notify does nothing:
notifyis ignored in a project-local.codex/config.toml; set it in~/.codex/config.toml. Codex passes the JSON as an argv string, not on stdin.
Development
npm ci
npm testnpm test compiles src/ and test/ to dist/ and runs the node:test suite. No network calls are made; the HTTP client tests use a local server.
License
MIT
Available Tools
3 toolsalarmA
Only for incidents that need the human NOW (production down, data loss, security). Rings the owner's phone: silent push to the app alarm, then a voice call if not acknowledged. Prefer notify_user for everything else. Calls repeat up to callAttempts times (default 3) until acknowledged. You decide when/whether to retry; one call = one escalation.
| Name | Required | Description | Default |
|---|---|---|---|
| call | No | Voice-call fallback (default true). With false the alarm ends expired if not acknowledged in the app | |
| title | Yes | Alarm title, read aloud on the call | |
| message | Yes | What happened and what is needed, read aloud on the call | |
| callAttempts | No | Voice-call attempts until acknowledged (1-5, default 3) | |
| waitForResult | No | Wait for a final status (default true); false returns {id, status} immediately | |
| maxWaitSeconds | No | Overall wait cap in seconds when waiting (default 600). On timeout the last status is returned with timedOut=true; re-check with alarm_status | |
| ackTimeoutSeconds | No | Seconds to wait for an app acknowledgement before calling | |
| deliveryTimeoutSeconds | No | Seconds to wait for the app to receive the alarm before calling |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden and does well: it discloses the escalation mechanism (silent push alarm then voice call), the retry/repeat behavior tied to callAttempts, and that the caller decides whether to retry ('one call = one escalation'). It omits what happens on final failure or how acknowledgement is signaled, keeping it from a 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Five tight sentences, front-loaded with the severity gate and escalation path, with the sibling alternative placed early. Slightly dense but every sentence carries distinct scope or behavioral information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given no annotations, no output schema, and 8 parameters, the description supplies the severity gate, escalation and retry behavior, and the sibling alternative. It leaves timeout parameter behavior to the schema, but the core invocation context an agent needs is present.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so baseline is 3, but the description adds cross-cutting meaning beyond the schema: it links callAttempts to the escalation loop and clarifies that repeat calls continue 'until acknowledged.' It does not explain waitForResult or maxWaitSeconds, so it does not reach 5.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific action (rings the owner's phone via silent push then voice call) and names the exact triggering conditions (production down, data loss, security). Explicitly routes everything else to the sibling notify_user, so it is distinguishable without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives an explicit exclusive criterion ('only for incidents that need the human NOW') and names the alternative ('Prefer notify_user for everything else'), leaving no ambiguity about when to choose this tool. No when-not exclusions are missing given the severity bar is stated.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
alarm_statusA
Check an alarm created by the alarm tool. With waitSeconds (max 50) it long-polls until the status changes. Use it to re-check after the alarm call timed out or was started with waitForResult=false.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | Alarm id returned by the alarm tool | |
| waitSeconds | No | Long-poll seconds, default 0 |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the burden and does add real behavior: waitSeconds triggers a long-poll that blocks until the status changes, capped at 50. It does not state rate limits, timeout/return behavior on expiry, or explicitly confirm the operation is read-only, so a small gap remains.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences with the core purpose front-loaded and no filler. The final sentence could be folded into the second without loss, but overall it is tight.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool is simple (one required id, one optional wait) and no output schema exists, so the description should arguably describe what a status result looks like or what happens when the poll expires. It covers the calling mechanics adequately but stops short of describing the returned status.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so baseline is 3, but the description adds meaning beyond the schema by explaining that waitSeconds drives a long-poll until the status changes rather than just being a numeric field. It still duplicates the max-50 bound already in the schema, keeping it from a 5.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb+resource ('Check an alarm created by the alarm tool') and names the sibling that produced it, so an agent can distinguish it from `alarm` itself. It is slightly less clear against `notify_user`, but the purpose is unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives an explicit when-to-use condition: 're-check after the alarm call timed out or was started with waitForResult=false.' This names the triggering scenario and the alternative path (waitForResult=true), leaving nothing to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
notify_userC
Send a phone push notification to the user via TMGR agent notifications.
| Name | Required | Description | Default |
|---|---|---|---|
| body | No | Notification body | |
| link | No | Absolute http(s) URL to open on tap | |
| title | Yes | Notification title | |
| priority | No | Priority, default normal |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full behavioral burden. It names the delivery channel ('via TMGR agent notifications') but does not say whether delivery is immediate or queued, whether it can fail silently, what happens with the optional link, or whether the recipient must be enrolled/authorized.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single efficient sentence with the action and channel front-loaded. 'TMGR agent' is unexplained internal jargon, which is a minor blemish but not padding.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 4-parameter tool with no annotations and no output schema, the schema fully covers inputs, so the description only needs to cover behavior. It leaves delivery semantics, failure modes, and the alarm-vs-notification distinction unexplained, making it minimally adequate rather than complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so all four parameters (title, body, link, priority) are already documented in the schema with constraints and an enum. The description adds only the implicit notion of a phone device, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description gives a specific verb (send) and resource (phone push notification) directed at the user, which is unambiguous. It does not, however, distinguish this from siblings alarm and alarm_status, leaving the agent to infer the boundary between a one-off push and a scheduled alarm.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no indication of when to reach for notify_user versus alarm or alarm_status, nor any stated preconditions or exclusions. The agent gets a capability statement but no selection guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
3 tool updates
v0.2.0- First observed
alarm - First observed
alarm_status - First observed
notify_user
TDQS
Scored across 3 tools
notify_user and alarm both push to the user's phone, so there is some surface overlap, but the descriptions sharply delineate when to use each (routine vs. urgent incident escalation). alarm_status is clearly a distinct read/check operation tied to the alarm tool.
notify_user follows a verb_noun pattern, but alarm is a bare noun and alarm_status is noun_noun. The convention is mixed, though all names remain readable and inferable from their descriptions.
Three tools is a tight, well-scoped set for a notification/escalation service: one soft notify, one hard escalation, one status check. It is slightly lean but each tool clearly earns its place.
The core lifecycle (notify, escalate, check status) is covered, but there is no tool to cancel/resolve an alarm, explicitly acknowledge it, or list past notifications/alarms. Agents must rely on out-of-band phone interaction to close out an escalation.
Related MCP Connectors
Reach your own phone from an AI agent: notifications, approval questions, reminders, ring, files.
- call-meOAuthapp.getcallme
Calls your phone when an AI task finishes or is blocked — hear it, say what's next.
Send mobile pings and route human questions, approvals, and handoffs from AI agents.
Build and send email, SMS, and push straight from your AI agent.
Related MCP Servers
- AlicenseBqualityDmaintenanceEnables AI assistants to send push notifications through the kweenkl service. Allows users to receive contextual notifications from their AI when tasks are complete or important events occur.110 npmMIT
- AlicenseAqualityCmaintenanceEnables AI assistants to send push notifications and interactive alerts to iPhone and Mac devices via the BotBell app. It allows AI to receive user replies and manage notification bots for tasks like alerts, reminders, and remote approvals.27 npm1MIT
- AlicenseNot gradedqualityDmaintenanceHuman-in-the-loop approvals and notifications for AI agents via WhatsApp. Enables Cursor, Claude Code, and autonomous AI agents to reach users away from their computers.60 npmISC
- AlicenseAqualityAmaintenanceEnables AI agents to send push alerts to your phone via Blipr, useful for notifying when tasks complete, builds break, or approvals are needed.5361 npmMIT