Self-hosted ntfy push notification server with an integrated MCP sidecar, designed for the Beacon service mesh.
Claude (or any MCP client) can send push notifications to your phone, desktop, or any ntfy subscriber — just by calling a tool.
┌──────────────────────────────────┐
│ ntfy-mcp container (mcp-net) │
│ │
│ ntfy serve → :8080 │ push notification server (Go)
│ mcp-sidecar → :9099 tcp │ MCP streamable HTTP endpoint
│ beacon announce → :9099 udp │ auto-discovery on mcp-net
└──────────────────────────────────┘
│ │
ntfy clients Beacon aggregator
(phone, browser) (http://localhost:9099/mcp)
Single container, two static Go binaries, no runtime dependencies.
- Docker
- Beacon running on
mcp-net(see Beacon README)
docker network create mcp-netcd ntfy
docker compose up -d --buildCheck the Beacon web UI at http://localhost:9300 — the ntfy server should appear with its tools.
Or test directly:
# Send a test notification via ntfy
curl -d "Hello from ntfy-mcp!" http://localhost:8080/test
# Check MCP health
curl http://localhost:9099/mcpInstall the ntfy app on your phone or use the web UI at http://localhost:8080 and subscribe to a topic.
Once discovered by Beacon, these tools are available as ntfy__<tool_name>:
| Tool | Description |
|---|---|
send_message |
Publish a notification to a topic |
send_photo |
Publish a notification with an image attachment |
list_messages |
Read cached messages from a topic |
server_info |
Health check and server version |
{
"topic": "alerts",
"message": "Build failed on main",
"title": "CI Alert",
"priority": 4,
"tags": ["warning", "build"],
"click": "https://github.com/org/repo/actions",
"actions": [
{ "action": "view", "label": "Open CI", "url": "https://ci.example.com" }
]
}Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
topic |
string | yes | Target topic name |
message |
string | yes | Notification body |
title |
string | no | Notification title |
priority |
int | no | 1 (min) to 5 (max), default 3 |
tags |
string[] | no | Emoji tags (e.g. ["warning"] → |
click |
string | no | URL to open on notification click |
actions |
object[] | no | Action buttons (view, http, broadcast) |
{
"topic": "photos",
"url": "https://example.com/screenshot.png",
"caption": "Latest dashboard screenshot"
}| Name | Type | Required | Description |
|---|---|---|---|
topic |
string | yes | Target topic name |
url |
string | yes | URL of the image to attach |
caption |
string | no | Message body alongside the image |
title |
string | no | Notification title |
priority |
int | no | 1-5, default 3 |
{
"topic": "alerts",
"since": "1h",
"limit": 10
}| Name | Type | Required | Description |
|---|---|---|---|
topic |
string | yes | Topic to read from |
since |
string | no | Duration like 30m, 1h, 1d or message ID. Default 1h |
limit |
int | no | Max messages to return. Default 50 |
No parameters. Returns ntfy server version, uptime, and topic count.
| Variable | Default | Description |
|---|---|---|
NTFY_BASE_URL |
http://localhost:8080 |
Internal ntfy server URL |
MCP_PORT |
9099 |
MCP sidecar HTTP + UDP port |
NTFY_DEFAULT_TOPIC |
general |
Default topic when none specified |
NTFY_AUTH_TOKEN |
(empty) | Bearer token for ntfy API (if auth enabled) |
services:
ntfy:
build: .
hostname: ntfy
ports:
- "8080:8080" # ntfy web UI + API (optional, for direct access)
expose:
- "9099/tcp" # MCP endpoint (Beacon discovers this)
- "9099/udp" # Beacon discovery
environment:
- NTFY_DEFAULT_TOPIC=general
volumes:
- ntfy-cache:/var/cache/ntfy # optional persistence
networks:
- mcp-net
volumes:
ntfy-cache:
networks:
mcp-net:
external: trueFor public-facing deployments behind nsl.sh:
Internet → nsl.sh (HTTPS) → nginx-hash-lock (:8080) → ntfy (:8080)
│
hash-based auth
- ntfy stays unauthenticated inside the Docker network
- nginx-hash-lock handles public access control via query parameter hash
- Beacon network is never exposed — MCP tools remain local-only
- ntfy clients (phone app) connect through the public nsl.sh URL with the auth hash
See Nginx-hash-lock for proxy configuration.
Once Beacon is running, ntfy tools are automatically available through the Beacon aggregator:
{
"mcpServers": {
"beacon": {
"type": "streamableHttp",
"url": "http://localhost:9099/mcp"
}
}
}Or via CLI:
claude mcp add beacon --transport http http://localhost:9099/mcpTools appear as ntfy__send_message, ntfy__send_photo, etc.
MIT