Skip to main content
Glama
tmgr-dev

tmgr-notify

Official
by tmgr-dev

@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_user sends a push notification.

  • alarm rings you for an urgent incident: an alarm in the app, then a voice call if you do not acknowledge it.

  • alarm_status re-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

TMGR_URL

no

API base URL, default https://api.tmgr.dev. Override it for a self-hosted or local TMGR. A base that already ends in /api is normalized.

TMGR_NOTIFY_TOKEN

yes

The tmgrn_... token. Sent as Authorization: Bearer <token>.

TMGR_NOTIFY_STOP_MIN_MINUTES

no

Minimum turn duration in minutes before hook stop sends a "finished" push. Default 5.

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 mcp

To 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 mcp

Or 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 titled Claude 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 types auth_success, elicitation_complete and elicitation_response are 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 if stop_hook_active is 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 least TMGR_NOTIFY_STOP_MIN_MINUTES (default 5) it sends a normal-priority push titled Claude 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:

  1. 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_unavailable if the app does not acknowledge it.

  2. Add the TMGR caller number to your contacts or Favorites so Focus / Do Not Disturb lets the call through.

  3. On iOS enable Repeated Calls (Settings → Focus → Do Not Disturb → Allow Calls From) so a second call within 3 minutes gets through.

  4. 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

title

string, required

1-120 characters.

body

string

Up to 1000 characters.

priority

low | normal | high

Default normal.

link

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

title

string, required

1-120 characters, read aloud on the call.

message

string, required

1-500 characters, read aloud on the call.

ackTimeoutSeconds

integer

Seconds to wait for an app acknowledgement before calling. The server clamps it to 15-900.

deliveryTimeoutSeconds

integer

Seconds to wait for the app to receive the alarm before calling. The server clamps it to 10-300.

call

boolean

Voice-call fallback, default true. With false the alarm ends expired if not acknowledged in the app.

callAttempts

integer 1-5

Voice-call attempts, default 3. The result includes attempts: made/max.

waitForResult

boolean

Default true. With false it returns {id, status} immediately.

maxWaitSeconds

number, max 3600

Wait cap, default 600. On timeout the last status is returned with timedOut: true.

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

id

string, required

Alarm id returned by alarm.

waitSeconds

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_TIMEOUT defaults to about 28 hours, so the 600 s default wait fits. A per-server timeout in .mcp.json overrides 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/progress after every poll when the client supplies a progressToken. For clients with a short hard cap, pass a small maxWaitSeconds (for example 45) or waitForResult: false, then call alarm_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 argument

To 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/env file (mode 600), and revoke it in TMGR if it leaks.

  • Agents cannot acknowledge alarms. Only you can, in the app or by pressing 1 on 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 0 by design. Check that your env vars or ~/.config/tmgr-notify/env are visible to the shell that Claude Code or Codex spawns (a login shell profile may not be sourced). Run the matching hook subcommand by hand with sample stdin or argv to see stderr.

  • hook stop never fires: short turns stay silent below TMGR_NOTIFY_STOP_MIN_MINUTES (default 5). Also confirm the UserPromptSubmit hook is configured; without it hook stop only 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-After value.

  • MCP tool not showing up in Claude Code: check claude mcp list, and remember mcpServers does not go in settings.json.

  • Codex notify does nothing: notify is 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 test

npm 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 tools
alarmA

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
callNoVoice-call fallback (default true). With false the alarm ends expired if not acknowledged in the app
titleYesAlarm title, read aloud on the call
messageYesWhat happened and what is needed, read aloud on the call
callAttemptsNoVoice-call attempts until acknowledged (1-5, default 3)
waitForResultNoWait for a final status (default true); false returns {id, status} immediately
maxWaitSecondsNoOverall wait cap in seconds when waiting (default 600). On timeout the last status is returned with timedOut=true; re-check with alarm_status
ackTimeoutSecondsNoSeconds to wait for an app acknowledgement before calling
deliveryTimeoutSecondsNoSeconds to wait for the app to receive the alarm before calling

TDQS

A4.5/5.0
Behavior4/5

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.

Conciseness4/5

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.

Completeness4/5

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.

Parameters4/5

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.

Purpose5/5

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.

Usage Guidelines5/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
idYesAlarm id returned by the alarm tool
waitSecondsNoLong-poll seconds, default 0

TDQS

A4.1/5.0
Behavior4/5

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.

Conciseness4/5

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.

Completeness3/5

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.

Parameters4/5

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.

Purpose4/5

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.

Usage Guidelines5/5

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.

ParametersJSON Schema
NameRequiredDescriptionDefault
bodyNoNotification body
linkNoAbsolute http(s) URL to open on tap
titleYesNotification title
priorityNoPriority, default normal

TDQS

C2.9/5.0
Behavior2/5

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.

Conciseness4/5

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.

Completeness3/5

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.

Parameters3/5

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.

Purpose4/5

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.

Usage Guidelines2/5

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.

  1. 3 tool updatesv0.2.0
    • First observedalarm
    • First observedalarm_status
    • First observednotify_user

TDQS

B3.4/5.0

Scored across 3 tools

Disambiguation4/5

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.

Naming Consistency3/5

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.

Tool Count4/5

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.

Completeness3/5

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

Related MCP Servers

  • A
    license
    B
    quality
    D
    maintenance
    Enables 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.
    1
    10 npm
    MIT
  • A
    license
    A
    quality
    C
    maintenance
    Enables 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.
    2
    7 npm
    1
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    Human-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 npm
    ISC
  • A
    license
    A
    quality
    A
    maintenance
    Enables AI agents to send push alerts to your phone via Blipr, useful for notifying when tasks complete, builds break, or approvals are needed.
    5
    361 npm
    MIT