Skip to main content
Glama
metaember

pushover-mcp

by metaember
README.md
# pushover-mcp

A tiny [MCP](https://modelcontextprotocol.io) server that sends push
notifications through [Pushover](https://pushover.net).

It exposes one tool:

- **`send_notification`** — send a `message` with optional `title`, `priority`,
  `sound`, `url`, `url_title`, and `device`.

Two parameters are designed to read well to an LLM:

- **`priority`** uses names instead of numbers: `lowest`, `low`, `normal`
  (default), `high`, `emergency` (repeats until acknowledged).
- **`sound`** is an allowlist fetched live from Pushover's sounds API **once at
  startup** and cached for the process. If that fetch fails (offline, or no
  token), it falls back to a baked-in list so the server still starts.

## Setup

1. Get your **user key** from your [Pushover dashboard](https://pushover.net)
   and create an application to get an **API token**
   at <https://pushover.net/apps/build>.
2. Install dependencies:

   ```sh
   uv sync
   ```

## Run

The server reads `PUSHOVER_TOKEN` and `PUSHOVER_USER` from the environment.

### With 1Password (recommended)

`.env` holds **secret references** (`op://…`), not the secrets themselves.
Launch through `op run` to resolve them at runtime:

```sh
op run --env-file=.env -- uv run pushover_mcp.py
```

### With plain env vars

```sh
PUSHOVER_TOKEN=... PUSHOVER_USER=... uv run pushover_mcp.py
```

## Use with Claude Code / Claude Desktop

Launching via `op run` keeps secrets out of the MCP config entirely:

```sh
claude mcp add pushover -- \
  op run --env-file=/Users/charlesbine/Documents/prog/mcp/pushover/.env -- \
  uv run --directory /Users/charlesbine/Documents/prog/mcp/pushover pushover_mcp.py
```

Or as JSON (e.g. `claude_desktop_config.json`):

```json
{
  "mcpServers": {
    "pushover": {
      "command": "op",
      "args": [
        "run", "--env-file=/Users/charlesbine/Documents/prog/mcp/pushover/.env", "--",
        "uv", "run", "--directory", "/Users/charlesbine/Documents/prog/mcp/pushover", "pushover_mcp.py"
      ]
    }
  }
}
```

## Tests

```sh
uv run pytest
```

Tests mock the network and use no real credentials, so they run offline.
Run them with plain `uv run pytest` (not via `op run`) so the import-time sound
fetch falls back instead of calling the live API. GitHub Actions runs the suite
on every push and pull request across Python 3.10–3.13 (see
`.github/workflows/ci.yml`).

## Run over HTTP (Docker)

The server speaks stdio by default. Set `MCP_TRANSPORT=streamable-http` to serve
over [Streamable HTTP](https://modelcontextprotocol.io) instead, so any
HTTP-capable MCP client can connect at `http://<host>:8000/mcp`. The Docker image
sets this for you.

```sh
docker build -t pushover-mcp .
docker run --rm -e PUSHOVER_TOKEN -e PUSHOVER_USER -p 8000:8000 pushover-mcp
```

As a Compose service — credentials live in this container's own environment (an
`env_file` or Docker secrets), so they stay isolated to this tool:

```yaml
services:
  pushover-mcp:
    build: .
    environment:
      PUSHOVER_TOKEN: ${PUSHOVER_TOKEN}
      PUSHOVER_USER: ${PUSHOVER_USER}
    restart: unless-stopped
```

The HTTP endpoint is unauthenticated, so don't expose it publicly — keep it on a
private/internal network (and drop the published `ports:` if a co-located client
reaches it over the Compose network).

## License

[MIT](LICENSE) © Charles Bine

TDQS

A3.9/5.0

Scored across 1 tool

Disambiguation5/5

Only one tool exists, so there is no possibility of confusion between tools.

Naming Consistency5/5

With a single tool, naming is trivially consistent.

Tool Count5/5

A single tool is appropriate for a focused server that sends push notifications via Pushover.

Completeness4/5

The tool covers the primary action of sending notifications, but lacks secondary features like delivery status or recipient management, which are minor gaps.

Maintenance

ActivityInactive
ResponsivenessNo issues