Skip to main content
Glama
ekomac

mcp-os-notifications

by ekomac
README.md
# mcp-os-notifications

An [MCP](https://modelcontextprotocol.io) server that lets LLM agents dispatch
native OS notifications when a task finishes. If you tell your agent, *"do this and
notify me when done"*, it will.

Works with **Claude Code**, **Claude Desktop**, **OpenCode**, or any other MCP client.

---

## What it does

- Exposes a single MCP tool: `dispatch_os_notification`.
- Detects when you ask to be notified (`"notify me when done"`, `"ping me when
  finished"`, etc.).
- Sends a native desktop notification once the agent finishes its work.
- **Task tokens**: reuse a token to update an existing notification instead of
  spamming new ones. Great for progress updates.
- **Mobile bridge**: optionally push to `ntfy.sh`, Telegram, or Pushover when you are
  away from your desk.
- **Quiet hours**: suppress non-critical desktop notifications during configured
  hours; critical ones still come through.
- Cross-platform: Linux (DBus/FreeDesktop notifications), macOS, Windows.

---

## How it works

1. The MCP server runs locally as a `stdio` subprocess of your agent.
2. Project-level agent instructions (`.claude/CLAUDE.md`, `skills/notify-when-done.md`)
   teach the agent when to call the tool.
3. When the work is done, the agent calls `dispatch_os_notification(title, message)`.
4. The server uses the native notification backend on your OS to show the toast/banner.

---

## Requirements

- Python 3.10+
- [uv](https://docs.astral.sh/uv/) (recommended for development) or `pip`
- A notification daemon on Linux (e.g., `mako`, `dunst`, GNOME/KDE built-in)

---

## Install on a new machine

### Option A: clone + `uv` (recommended for development)

```bash
git clone https://github.com/ekomac/mcp-os-notifications.git
cd mcp-os-notifications
uv sync
uv run mcp-os-notifications
```

### Option B: clone + `pip`

```bash
git clone https://github.com/ekomac/mcp-os-notifications.git
cd mcp-os-notifications
python3 -m venv .venv
source .venv/bin/activate
pip install -e .
mcp-os-notifications
```

### Option C: install from GitHub without cloning

```bash
python3 -m pip install git+https://github.com/ekomac/mcp-os-notifications.git
mcp-os-notifications
```

> **Note:** If you install from PyPI in the future (once published), the command is
> simply `pip install mcp-os-notifications`.

### Verify the server starts

The server runs over stdio and waits for MCP messages. Press `Ctrl+C` to stop.

```bash
# If using uv in the repo
uv run mcp-os-notifications

# If installed globally with pip
mcp-os-notifications
```

---

## Add to Claude Code

Claude Code reads `.mcp.json` at project scope or `~/.claude.json` at user scope.

### Project scope (recommended per repo)

If you open this repository directly in Claude Code, the bundled `.mcp.json` already
registers the server. Just ask:

```
Build a quick Python script that prints the current time and notify me when done.
```

To use it in a **different project** while keeping the source in this repo, copy the
snippet below into that project's `.mcp.json` and replace `/ABSOLUTE/PATH/TO/mcp-os-notifications`:

```json
{
  "mcpServers": {
    "os-notifications": {
      "type": "stdio",
      "command": "uv",
      "args": [
        "--directory",
        "/ABSOLUTE/PATH/TO/mcp-os-notifications",
        "run",
        "mcp-os-notifications"
      ]
    }
  }
}
```

### User scope (global for all projects)

If you installed globally with pip or uv tool, add the command directly:

```bash
claude mcp add --transport stdio os-notifications mcp-os-notifications
```

Or if you keep the cloned repo:

```bash
claude mcp add --transport stdio os-notifications uv --directory /ABSOLUTE/PATH/TO/mcp-os-notifications run mcp-os-notifications
```

### Check that it loaded

```bash
claude mcp list
```

You should see `os-notifications` with the `dispatch_os_notification` and `notify_user`
tools.

---

## Add to Claude Desktop

Add the server to `claude_desktop_config.json`:

- **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
- **Windows:** `%AppData%\Claude\claude_desktop_config.json`
- **Linux:** `~/.config/Claude/claude_desktop_config.json` (or
  `$XDG_CONFIG_HOME/Claude/claude_desktop_config.json`)

If installed globally with pip:

```json
{
  "mcpServers": {
    "os-notifications": {
      "command": "mcp-os-notifications"
    }
  }
}
```

If using the cloned repo with uv:

```json
{
  "mcpServers": {
    "os-notifications": {
      "command": "uv",
      "args": [
        "--directory",
        "/ABSOLUTE/PATH/TO/mcp-os-notifications",
        "run",
        "mcp-os-notifications"
      ]
    }
  }
}
```

---

## Add to OpenCode

OpenCode supports MCP servers via stdio. Add this to your OpenCode MCP configuration:

```json
{
  "mcpServers": {
    "os-notifications": {
      "command": "mcp-os-notifications"
    }
  }
}
```

If you run from the cloned repo instead:

```json
{
  "mcpServers": {
    "os-notifications": {
      "command": "uv",
      "args": [
        "--directory",
        "/ABSOLUTE/PATH/TO/mcp-os-notifications",
        "run",
        "mcp-os-notifications"
      ]
    }
  }
}
```

Then copy the skill reference so OpenCode knows when to use the tool:

```bash
mkdir -p ~/.config/opencode/skills/notify-when-done
cp skills/notify-when-done.md ~/.config/opencode/skills/notify-when-done/SKILL.md
```

---

## Other MCP clients

Any client that supports stdio MCP servers can use one of these commands:

```bash
# Installed globally
mcp-os-notifications

# From the cloned repo with uv
uv --directory /ABSOLUTE/PATH/TO/mcp-os-notifications run mcp-os-notifications

# From the cloned repo with pip venv
.venv/bin/mcp-os-notifications
```

---

## Tool schema

The server exposes two identical tools; `notify_user` is a convenience alias for
`dispatch_os_notification`.

### `dispatch_os_notification`

| Parameter | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `title` | string | yes | — | Short notification title |
| `message` | string | yes | — | Notification body |
| `urgency` | string | no | `"normal"` | `"low"`, `"normal"`, or `"critical"` |
| `sound` | boolean | no | `false` | Play an audible alert |
| `timeout` | integer | no | `-1` | Auto-dismiss after N seconds; `-1` uses OS default |
| `token` | string | no | `null` | Stable id; reusing it replaces the previous notification |
| `mobile` | boolean | no | `false` | Also push to configured mobile backends |

### Example tool calls

#### Simple completion notification

```json
{
  "title": "Build finished",
  "message": "All 42 tests passed.",
  "urgency": "normal",
  "sound": true
}
```

#### Progress updates with a token

```json
{
  "title": "Syncing files...",
  "message": "25 of 100 files processed",
  "token": "file-sync-job"
}
```

Then later with the same token:

```json
{
  "title": "Sync complete",
  "message": "All 100 files uploaded",
  "token": "file-sync-job"
}
```

#### Critical + mobile push

```json
{
  "title": "Build failed",
  "message": "Production deployment failed on the test stage.",
  "urgency": "critical",
  "mobile": true
}
```

---

## Mobile bridge

The server can push notifications to mobile/remote backends alongside the desktop toast.
This is useful when you are away from the computer. Configure one or more backends via
environment variables.

### ntfy.sh

```bash
export MCP_OS_NOTIFICATIONS_NTFY_URL="https://ntfy.sh/my-secret-topic"
```

You can also use a self-hosted ntfy instance:

```bash
export MCP_OS_NOTIFICATIONS_NTFY_URL="https://ntfy.example.com/alerts"
```

### Telegram

```bash
export MCP_OS_NOTIFICATIONS_TELEGRAM_BOT_TOKEN="123456:ABC..."
export MCP_OS_NOTIFICATIONS_TELEGRAM_CHAT_ID="123456789"
```

### Pushover

```bash
export MCP_OS_NOTIFICATIONS_PUSHOVER_USER_KEY="uQiRz..."
export MCP_OS_NOTIFICATIONS_PUSHOVER_APP_TOKEN="azGDO..."
```

### Usage

Once a backend is configured, set `mobile: true` in the tool call. The server will
notify every configured backend in parallel.

### Passing environment variables to the server

Mobile and quiet-hours settings are read from the environment of the MCP server process.
How you set them depends on the client:

- **Claude Code:** environment variables are inherited from the shell where you launched
  `claude`, or you can set them in the `.mcp.json` `env` block:

  ```json
  {
    "mcpServers": {
      "os-notifications": {
        "type": "stdio",
        "command": "mcp-os-notifications",
        "env": {
          "MCP_OS_NOTIFICATIONS_NTFY_URL": "https://ntfy.sh/my-topic"
        }
      }
    }
  }
  ```

- **Claude Desktop:** use the `env` block in `claude_desktop_config.json`:

  ```json
  {
    "mcpServers": {
      "os-notifications": {
        "command": "mcp-os-notifications",
        "env": {
          "MCP_OS_NOTIFICATIONS_NTFY_URL": "https://ntfy.sh/my-topic"
        }
      }
    }
  }
  ```

- **OpenCode / other clients:** set the variables in the shell before launching the
  client, or use the client's equivalent MCP environment settings.

---

## Quiet hours

Suppress non-critical desktop notifications during a configured time window. Critical
notifications still come through.

```bash
export MCP_OS_NOTIFICATIONS_QUIET_START="22:00"
export MCP_OS_NOTIFICATIONS_QUIET_END="07:00"
```

The window can span midnight (e.g., 22:00 to 07:00). Mobile pushes are not affected by
quiet hours.

See [Passing environment variables to the server](#passing-environment-variables-to-the-server)
above for how to expose these variables to the MCP server process.

---

## Project structure

```
.
├── src/mcp_os_notifications/
│   ├── server.py                # MCP server and tools
│   ├── mobile.py                # Mobile/remote notification backends
│   └── quiet_hours.py           # Do-not-disturb logic
├── skills/notify-when-done.md   # Generic agent skill reference
├── .claude/CLAUDE.md            # Claude Code project instructions
├── .mcp.json                    # Project-scoped Claude Code MCP config
├── .github/workflows/ci.yml     # GitHub Actions CI
├── Makefile                     # Common dev commands
├── pyproject.toml
├── README.md
└── LICENSE
```

---

## Development

A `Makefile` is provided for convenience:

```bash
make install   # uv sync
make test      # uv run pytest
make lint      # uv run ruff check .
make run       # uv run mcp-os-notifications
make clean     # remove caches, venv, build artifacts
```

CI runs on GitHub Actions for Python 3.10–3.14.

---

## Troubleshooting

### Linux: "DBUS_SESSION_BUS_ADDRESS not set"

The server tries to auto-detect the session bus, but if that fails, make sure your
notification daemon is running (e.g., `mako`, `dunst`, `swaync`). You can also set:

```bash
export DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/$(id - u)/bus"
```

### No notification appears

1. Check that your OS has a notification service enabled.
2. Try running the server directly with verbose logging:
   ```bash
   MCP_OS_NOTIFICATIONS_LOG_LEVEL=DEBUG uv run mcp-os-notifications
   ```
3. Check your agent's tool call output for the `success` flag.

---

## Publishing / adding to the MCP ecosystem

There is no "Anthropic marketplace" for MCP servers, but there are official and
community registries:

| Registry | How to submit |
| --- | --- |
| [MCP Registry](https://registry.modelcontextprotocol.io) | Official registry. Use `mcp-publisher` CLI to publish `server.json` metadata. |
| [Claude Connectors Directory](https://claude.ai/directory) | Anthropic-reviewed connectors. Submit via the directory form for remote servers or desktop extensions. |
| [Smithery](https://smithery.ai) | Third-party MCP registry/marketplace. |
| [Glama](https://glama.ai/mcp/servers) | Indexes the official MCP Registry automatically. |

`modelcontextprotocol/servers` no longer accepts third-party listings.

---

## Prior art

This is not the first MCP notification server. Similar projects exist, e.g.:

- [Cactusinhand/mcp_server_notify](https://github.com/Cactusinhand/mcp_server_notify)
- [pinkpixel-dev/notification-mcp](https://github.com/pinkpixel-dev/notification-mcp)
- [devizor/macOS-Notification-MCP](https://github.com/devizor/macOS-Notification-MCP)

This repo focuses on:

- A clean, minimal Python implementation using `desktop-notifier`.
- Ready-to-use agent skill references for Claude Code and OpenCode.
- A project-scoped `.mcp.json` so Claude Code works out of the box.
- **Task tokens** to replace notifications instead of spamming.
- **Mobile bridge** to ntfy.sh, Telegram, and Pushover.
- **Quiet hours** to respect do-not-disturb time windows.

---

## License

MIT — see [LICENSE](LICENSE).

TDQS

B3.2/5.0

Scored across 2 tools

Disambiguation1/5

The two tools are actually the same; notify_user is explicitly an alias for dispatch_os_notification. This creates complete overlap and makes it impossible for an agent to distinguish between them.

Naming Consistency4/5

Both tool names follow a verb_noun pattern (dispatch_os_notification, notify_user), so the naming convention is consistent. However, the presence of a duplicate alias reduces the overall pattern's clarity.

Tool Count4/5

With only 2 tools (one of which is a duplicate), the count is minimal but still reasonable for a focused notification dispatch server. It is not excessive, but could be consolidated into a single tool.

Completeness4/5

The server covers the core functionality of dispatching OS notifications with configurable options (urgency, sound, timeout, mobile). However, it lacks any management or querying capabilities, which could be considered a minor gap.

Maintenance

ActivityStale
ResponsivenessNo issues