ntfy-me-mcp
# <img src="https://m2tg1pnwn0.ufs.sh/f/GMqNN8nd9I8l9tUbmif1CnFX8Baqr7mHeicYu0AULDyNVWJE" width=30 /> ntfy-me-mcp
[](https://www.typescriptlang.org/)
[](https://modelcontextprotocol.io/)
[](https://www.npmjs.com/package/ntfy-me-mcp)
[](https://hub.docker.com/r/gitmotion/ntfy-me-mcp)
[](LICENSE)
[](https://github.com/gitmotion/ntfy-me-mcp)
<a href="https://www.buymeacoffee.com/gitmotion" target="_blank" rel="noopener noreferrer">
<img src="https://www.buymeacoffee.com/assets/img/custom_images/yellow_img.png" alt="Buy me a coffee" width="105px" />
</a>
> A streamlined Model Context Protocol (MCP) server for sending and fetching notifications via the ntfy service (public or self-hosted, with token support) š²
## Overview
ntfy-me-mcp provides AI assistants with the ability to send real-time notifications to your devices through the [ntfy.sh](https://ntfy.sh) service (either public or selfhosted with token support). Get notified when your AI completes tasks, encounters errors, or reaches important milestones - all without constant monitoring.
The server includes intelligent features like automatic URL detection for creating view actions and smart markdown formatting detection, making it easier for AI assistants to create rich, interactive notifications without extra configuration.
<table>
<thead>
<tr>
<th width="50%">Preview</th>
<th width="50%">Available via</th>
</tr>
</thead>
<tbody>
<tr>
<td valign="top"><img src="https://m2tg1pnwn0.ufs.sh/f/GMqNN8nd9I8lvhAeasbt6OQorL7fKJdgMSekE0Wanp5HXNIm" alt="autodetect-preview" width="100%"></td>
<td valign="top">
<table>
<thead>
<tr><th>Name</th><th>Link / Badge</th></tr>
</thead>
<tbody>
<tr>
<td>ntfy.sh</td>
<td><a href="https://docs.ntfy.sh/integrations/#projects-scripts">Featured on ntfy.sh</a></td>
</tr>
<tr>
<td>Glama.ai</td>
<td><a href="https://glama.ai/mcp/servers/@gitmotion/ntfy-me-mcp"><img width="250" src="https://glama.ai/mcp/servers/@gitmotion/ntfy-me-mcp/badge" alt="ntfy-me-mcp MCP server" /></a></td>
</tr>
<tr>
<td>LightNow</td>
<td><a href="https://lightnow.ai/servers/io.github.gitmotion/ntfy-me-mcp"><img src="https://lightnow.ai/badge/io.github.gitmotion/ntfy-me-mcp" alt="LightNow capabilities" /></a></td>
</tr>
<tr>
<td>MseeP.ai</td>
<td><a href="https://mseep.ai/app/gitmotion-ntfy-me-mcp"><img width="150" src="https://mseep.net/pr/gitmotion-ntfy-me-mcp-badge.png" alt="ntfy-me-mc-mseepai" /></a></td>
</tr>
<tr>
<td>Archestra.ai</td>
<td><a href="https://archestra.ai/mcp-catalog/gitmotion__ntfy-me-mcp"><img src="https://archestra.ai/mcp-catalog/api/badge/quality/gitmotion/ntfy-me-mcp" alt="Trust Score" /></a></td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
## Features
- š **Quick Setup**: Run with npx or docker!
- š **Real-time Notifications**: Get updates on your phone/desktop when tasks complete
- šØ **Rich Notifications**: Support for topic, title, priorities, emoji tags, and detailed messages
- š **Notification Fetching**: Fetch and filter cached messages from your ntfy topics
- šÆ **Smart Action Links**: Automatically detects URLs in messages and creates view actions
- š **Click URLs**: Choose the link that opens when the notification is tapped
- š **Intelligent Markdown**: Auto-detects and enables markdown formatting when present
- š **Secure**: Optional authentication with access tokens
- š **Input Masking**: Securely store your ntfy token in your vs config!
- š **Self-hosted Support**: Works with both ntfy.sh and self-hosted ntfy instances
#### Coming soon...
- šØ **Email**: Send notifications to email (requires ntfy email server configuration)
- š¼ļø **Image urls**: Intelligent image url detection to automatically include image urls in messages and notifications
- š and more!
## Table of Contents
<table>
<thead>
<tr>
<th>Section</th>
<th>Topics</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="#quickstart---mcp-server-configuration">Quickstart - MCP Server Configuration</a></td>
<td>
<a href="#configuration-examples">Configuration Examples</a>
</td>
</tr>
<tr>
<td><a href="#installation">Installation</a></td>
<td>
<a href="#setting-up-the-notification-receiver">Setting Up the Notification Receiver</a>
</td>
</tr>
<tr>
<td><a href="#configuration">Configuration</a></td>
<td>
<a href="#environment-variables">Environment Variables</a><br/>
<a href="#authentication">Authentication</a><br/>
<a href="#secure-token-handling-vscode">ā³ Secure Token Handling (vscode)</a>
</td>
</tr>
<tr>
<td><a href="#tools--usage">Tools & Usage</a></td>
<td>
<a href="#ntfy_me-sending-notifications">ntfy_me: Sending Notifications</a><br/>
<a href="#using-natural-language">ā³ Using Natural Language</a><br/>
<a href="#example-usage">ā³ Example Usage</a><br/>
<a href="#message-parameters">ā³ Message Parameters</a><br/>
<a href="#ntfy_me_fetch-polling-notifications">ntfy_me_fetch: Polling Notifications</a><br/>
<a href="#using-natural-language-1">ā³ Using Natural Language</a><br/>
<a href="#example-usage-1">ā³ Example Usage</a><br/>
<a href="#fetch-parameters">ā³ Fetch Parameters</a><br/>
<a href="#tool-output">Tool output</a>
</td>
</tr>
<tr>
<td><a href="#security">Security</a></td>
<td></td>
</tr>
<tr>
<td><a href="#development--contributions">Development & Contributions</a></td>
<td></td>
</tr>
<tr>
<td><a href="#license">License</a></td>
<td></td>
</tr>
</tbody>
</table>
## Quickstart - MCP Server Configuration
Choose the config shape that matches your client. All examples below use `NTFY_TOPIC` as the required variable and keep the optional auth settings commented out until you need them.
> **Tested with Ontheia:** [Ontheia](https://ontheia.ai), a self-hosted, open-source AI agent platform, reports that ntfy-me-mcp works with it and lists it among its [compatible MCP servers](https://docs.ontheia.ai/en/getting-started/03_compatible-mcp-servers/).
> **Requirements:** Node.js 24 or newer for the npx, global and local options. CI and the Docker image use Node 24. Node 22 and earlier aren't supported (npm warns `EBADENGINE`). The Docker option needs only Docker.
### Configuration Examples
<table>
<thead>
<tr>
<th width="20%">Type</th>
<th width="40%">Use Case</th>
<th width="40%">Example</th>
</tr>
</thead>
<tbody>
<tr>
<td>NPM / NPX</td>
<td>Recommended for most MCP clients when you want the lightest setup.</td>
<td>
<details>
<summary>Show config</summary>
<pre><code>{
"ntfy-me-mcp": {
"command": "npx",
"args": ["-y", "ntfy-me-mcp"],
"env": {
"NTFY_TOPIC": "your-ntfy-topic",
"NTFY_URL": "https://ntfy.sh",
// "NTFY_TOKEN": "add-your-ntfy-token"
}
}
}</code></pre>
</details>
</td>
</tr>
<tr>
<td>Local</td>
<td>Use a local checkout when you are developing or changing the server yourself.
<br>Replace <code>/absolute/path/to/ntfy-me-mcp/build/index.js</code> after building.
</td>
<td>
<details>
<summary>Show config</summary>
<pre><code>{
"ntfy-me-mcp": {
"command": "node",
"args": ["/absolute/path/to/ntfy-me-mcp/build/index.js"],
"env": {
"NTFY_TOPIC": "your-ntfy-topic",
"NTFY_URL": "https://ntfy.sh",
// "NTFY_TOKEN": "add-your-ntfy-token"
}
}
}</code></pre>
</details>
</td>
</tr>
<tr>
<td>Docker</td>
<td>Use a containerized setup when Docker is already part of your environment.
<br/> - DockerHub: <code>gitmotion/ntfy-me-mcp:latest</code><br/> - GHCR: <code>ghcr.io/gitmotion/ntfy-me-mcp:latest</code>
</td>
<td>
<details>
<summary>Show config</summary>
<pre><code>{
"ntfy-me-mcp": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"-e",
"NTFY_TOPIC",
"-e",
"NTFY_URL",
"-e",
"NTFY_TOKEN",
"gitmotion/ntfy-me-mcp:latest"
],
"env": {
"NTFY_TOPIC": "your-ntfy-topic",
"NTFY_URL": "https://ntfy.sh",
// "NTFY_TOKEN": "add-your-ntfy-token"
}
}
}</code></pre>
</details>
</td>
</tr>
<tr>
<td>OpenCode</td>
<td>Add to <code>opencode.json</code> in your project root (for project-level config) or <code>~/.config/opencode/opencode.json</code> (for global config). Uses <code>"mcp"</code> as the top-level key with <code>type: "local"</code> and <code>command</code> as an array.</td>
<td>
<details>
<summary>Show config</summary>
<pre><code>{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"ntfy-me-mcp": {
"type": "local",
"command": ["npx", "-y", "ntfy-me-mcp"],
"environment": {
"NTFY_TOPIC": "your-ntfy-topic",
"NTFY_URL": "https://ntfy.sh",
// "NTFY_TOKEN": "add-your-ntfy-token"
}
}
}
}</code></pre>
</details>
</td>
</tr>
<tr>
<td>Claude Code</td>
<td>Quickest: run <code>claude mcp add ntfy-me-mcp -e NTFY_TOPIC=your-ntfy-topic -- npx -y ntfy-me-mcp</code> (add <code>--scope user</code> for all projects, or <code>--scope project</code> to write <code>.mcp.json</code>).
<br/>Or add it by hand to <code>.mcp.json</code> at your project root (shared with your team via version control). <code>${NTFY_TOKEN:-}</code> reads the token from your shell environment and falls back to empty.</td>
<td>
<details>
<summary>Show config</summary>
<pre><code>{
"mcpServers": {
"ntfy-me-mcp": {
"command": "npx",
"args": ["-y", "ntfy-me-mcp"],
"env": {
"NTFY_TOPIC": "your-ntfy-topic",
"NTFY_URL": "https://ntfy.sh",
"NTFY_TOKEN": "${NTFY_TOKEN:-}"
}
}
}
}</code></pre>
</details>
</td>
</tr>
<tr>
<td>Copilot CLI</td>
<td>Add to <code>~/.copilot/mcp-config.json</code> for user-level access across all sessions. Use <code>type: "local"</code> for stdio-based servers like this one.</td>
<td>
<details>
<summary>Show config</summary>
<pre><code>{
"mcpServers": {
"ntfy-me-mcp": {
"type": "local",
"command": "npx",
"args": ["-y", "ntfy-me-mcp"],
"env": {
"NTFY_TOPIC": "your-ntfy-topic",
"NTFY_URL": "https://ntfy.sh",
"NTFY_TOKEN": "your-access-token"
},
"tools": ["*"]
}
}
}</code></pre>
</details>
</td>
</tr>
<tr>
<td>Codex</td>
<td>Run <code>codex mcp add ntfy-me-mcp --env NTFY_TOPIC=your-ntfy-topic -- npx -y ntfy-me-mcp</code>, or add the block to <code>~/.codex/config.toml</code> by hand.</td>
<td>
<details>
<summary>Show config</summary>
<pre><code>[mcp_servers.ntfy-me-mcp]
command = "npx"
args = ["-y", "ntfy-me-mcp"]
[mcp_servers.ntfy-me-mcp.env]
NTFY_TOPIC = "your-ntfy-topic"
NTFY_URL = "https://ntfy.sh"
# NTFY_TOKEN = "your-access-token"</code></pre>
</details>
</td>
</tr>
<tr>
<td>Token Auth</td>
<td>Required for protected topics or self-hosted servers. See <a href="#secure-token-handling-vscode">Secure Token Handling (vscode)</a> or set <code>NTFY_TOKEN</code> directly.</td>
<td>
<details>
<summary>Show config</summary>
<pre><code>{
"ntfy-me-mcp": {
"command": "npx",
"args": ["-y", "ntfy-me-mcp"],
"env": {
"NTFY_TOPIC": "your-ntfy-topic",
"NTFY_URL": "https://your-ntfy-server.com",
"NTFY_TOKEN": "your-access-token"
}
}
}</code></pre>
</details>
</td>
</tr>
</tbody>
</table>
## Installation
If you need to install and run the server directly (alternative to the MCP configuration above):
<table>
<thead>
<tr>
<th>Option</th>
<th>Example</th>
</tr>
</thead>
<tbody>
<tr>
<td valign="top"><b>Install globally</b><br/>Install once, run anywhere with the <code>ntfy-me-mcp</code> command.</td>
<td>
<code>npm install -g ntfy-me-mcp</code>
</td>
</tr>
<tr>
<td valign="top"><b>Run with npx</b><br/>No install needed ā ideal for a quick one-off run or testing.</td>
<td>
<code>npx ntfy-me-mcp</code>
</td>
</tr>
<tr>
<td valign="top"><b>Install locally</b><br/>Clone the repo, install deps, build, and run via <code>npm start</code>.</td>
<td>
<details>
<summary>Show steps</summary>
<pre><code># Clone repo, install deps, configure .env, build, run<br/>
git clone https://github.com/gitmotion/ntfy-me-mcp.git
cd ntfy-me-mcp
npm install
cp .env.example .env
npm run build
npm start
</code></pre>
</details>
</td>
</tr>
</tbody>
</table>
### Setting Up the Notification Receiver
<details>
<summary>View ntfy receiver Section</summary>
1. Install the [ntfy app](https://ntfy.sh/app) on your device
- or deploy your own ntfy server: https://docs.ntfy.sh/install/
2. Subscribe to your chosen topic (the same as your `NTFY_TOPIC` setting)
</details>
## Configuration
### Environment Variables
Create a `.env` file by copying the example: `cp .env.example .env` ā see [`.env.example`](.env.example) for reference.
| Variable | Required | Default | Description |
| --- | --- | --- | --- |
| `NTFY_TOPIC` | Yes | ā | The ntfy topic to publish notifications to |
| `NTFY_URL` | No | `https://ntfy.sh` | ntfy server URL ā change this for self-hosted instances<br/>(include port if needed, e.g. `https://your-server.com:8443`) |
| `NTFY_TOKEN` | No | ā | Access token for protected topics or private servers. Only ever sent to the `NTFY_URL` server |
| `NTFY_ALLOW_TOPIC_OVERRIDE` | No | `false` | Set to `true` to let the agent choose a per-call `topic`. When off (the default), the `topic` parameter isn't offered to the agent and every notification and fetch uses `NTFY_TOPIC` |
| `NTFY_TOPICS_ALLOWLIST` | No | ā | Comma-separated topics you subscribe to, e.g. `alerts,builds,deploys`. When set, the agent may send to or fetch from `NTFY_TOPIC` or one of these topics, and no other topic. The `topic` parameter is offered as a list of exactly those topics. Takes precedence over `NTFY_ALLOW_TOPIC_OVERRIDE`. An invalid entry, or a value that lists no topics (e.g. `,`), stops the server at startup |
| `NTFY_ALLOW_URL_OVERRIDE` | No | `false` | Set to `true` to let the agent choose a per-call server `url`. When off (the default), the `url` parameter isn't offered and every request goes to `NTFY_URL`. Even when on, `NTFY_TOKEN` is only sent to `NTFY_URL` |
### Authentication
<details>
<summary>View Authentication Section</summary>
This MCP server supports both authenticated and unauthenticated ntfy endpoints:
- **Public Topics**: When using public topics on ntfy.sh or other public servers, no authentication is required.
- **Protected Topics**:
- For protected topics or private servers, you need to provide an access token via `NTFY_TOKEN` env variable or in the `accessToken` parameter of the tool.
- If authentication is required but not provided, you'll receive a clear error message explaining how to add your token.
#### Secure Token Handling (vscode)
- If your client supports prompt-based secret inputs (i.e. VS Code), prefer that over hardcoding `NTFY_TOKEN` in config files. (Otherwise use your token directly)
- Use matching values like this in your `mcp.json` file:
<details>
<summary>Show VS Code mcp.json example</summary>
```jsonc
// Add this to your VS Code `mcp.json` file, either the user-level file or your workspace `.vscode/mcp.json`
// Set `NTFY_TOKEN` exactly to `"${input:ntfy_token}"` when you want VS Code to treat it as a secure prompt-backed value.
{
"inputs": [
{
"type": "promptString",
"id": "ntfy_token",
"description": "Ntfy Token",
"password": true
}
],
"servers": {
"ntfy-me-mcp": {
"command": "npx",
"args": ["-y", "ntfy-me-mcp"],
"env": {
"NTFY_TOPIC": "your-ntfy-topic",
"NTFY_URL": "https://your-ntfy-server.com",
"NTFY_TOKEN": "${input:ntfy_token}"
}
}
}
}
```
<br/>
</details>
| Field | Value | Purpose |
| --- | --- | --- |
| `env.NTFY_TOKEN` | `"${input:ntfy_token}"` | References the secure prompt-backed token value |
| `inputs[].id` | `"ntfy_token"` | Defines the input name used by `NTFY_TOKEN` |
| `inputs[].type` | `"promptString"` | Prompts the user for the token at runtime |
> Your MCP client must resolve `"${input:ntfy_token}"` before it launches the server, as VS Code does, so the server receives the real token. If your client doesn't support `${input:ā¦}` references (Claude Desktop, Claude Code, Codex and others), set `NTFY_TOKEN` to the real token or to an environment-variable reference your client does support. If the placeholder reaches the server unresolved, the server can't ask you for the token (stdin and stdout carry the MCP connection), so it exits at startup with an error saying `NTFY_TOKEN is an unresolved ${input:ā¦} reference`, which your client shows in its MCP server log. The same goes for any other placeholder your client didn't substitute, such as `${env:NTFY_TOKEN}` or `${NTFY_TOKEN}`: the server exits with `NTFY_TOKEN contains an unresolved ${ā¦} placeholder` instead of sending it as a token, which ntfy servers with auth enabled (such as ntfy.sh) reject on every request.
>
> The `PROTECTED_TOPIC` env var was removed in `v1.4.0`. For protected topics, set `NTFY_TOKEN`, resolved by your client, instead.
</details>
## Tools & Usage
### `ntfy_me`: Sending Notifications
#### Using Natural Language
- When working with your AI assistant, you can use natural phrases to request notifications:
```
"ntfyme with a summary of the task when complete"
"Send me a notification when the build is complete"
"Notify me when the task is done"
"Alert me after generating the code"
"Message me when the process finishes"
"Send an alert with high priority"
```
#### Example Usage
<table>
<thead>
<tr>
<th width="50%">Input</th>
<th width="50%">Output</th>
</tr>
</thead>
<tbody>
<tr>
<td valign="top"><pre><code>{
"title": "Code Generation Complete",
"message": "Your React component has been
created successfully with proper
TypeScript typing.",
"priority": "high",
"tags": ["white_check_mark", "code", "react"]
}</code></pre></td>
<td valign="top"><pre><code>{
"success": true,
"endpoint": "https://ntfy.sh/ntfymetest"
}</code></pre></td>
</tr>
</tbody>
</table>
#### Message Parameters
<table>
<thead>
<tr>
<th width="10%">Parameter</th>
<th width="40%">Description</th>
<th width="10%">Required</th>
<th width="40%">Details / Example</th>
</tr>
</thead>
<tbody>
<tr>
<td valign="top"><code>title</code></td>
<td valign="top">The notification title. Unicode and emoji are supported</td>
<td valign="top">Yes</td>
<td valign="top">ā</td>
</tr>
<tr>
<td valign="top"><code>message</code></td>
<td valign="top">The notification body</td>
<td valign="top">Yes</td>
<td valign="top">ā</td>
</tr>
<tr>
<td valign="top"><code>url</code></td>
<td valign="top">Custom ntfy server URL. <b>Only offered when <code>NTFY_ALLOW_URL_OVERRIDE=true</code></b>. <code>NTFY_TOKEN</code> is only sent to the <code>NTFY_URL</code> server; pass <code>accessToken</code> for another server</td>
<td valign="top">No</td>
<td valign="top"><i>Default: <code>NTFY_URL</code></i></td>
</tr>
<tr>
<td valign="top"><code>topic</code></td>
<td valign="top">Custom ntfy topic. <b>Only offered when <code>NTFY_TOPICS_ALLOWLIST</code> is set (then limited to <code>NTFY_TOPIC</code> and those topics) or <code>NTFY_ALLOW_TOPIC_OVERRIDE=true</code></b>; otherwise every notification goes to <code>NTFY_TOPIC</code></td>
<td valign="top">No</td>
<td valign="top"><i>Default: <code>NTFY_TOPIC</code></i></td>
</tr>
<tr>
<td valign="top"><code>accessToken</code></td>
<td valign="top">Access token for protected topics. A blank value (<code>""</code>, spaces) counts as not provided</td>
<td valign="top">No</td>
<td valign="top"><i>Default: <code>NTFY_TOKEN</code></i></td>
</tr>
<tr>
<td valign="top"><code>priority</code></td>
<td valign="top">Message priority level</td>
<td valign="top">No</td>
<td valign="top"><i>Default: <code>"default"</code></i><br/>Options: <code>min</code>, <code>low</code>, <code>default</code>, <code>high</code>, <code>max</code></td>
</tr>
<tr>
<td valign="top"><code>tags</code></td>
<td valign="top">Array of notification tags. Supports emoji shortcodes for visual indicators ā see the <a href="https://docs.ntfy.sh/emojis/">full list</a>.</td>
<td valign="top">No</td>
<td valign="top">
<code>warning</code> ā ā ļø<br/>
<code>white_check_mark</code> ā ā
<br/>
<code>rocket</code> ā š<br/>
<code>tada</code> ā š
</td>
</tr>
<tr>
<td valign="top"><code>markdown</code></td>
<td valign="top">Boolean to enable markdown formatting. Auto-detected when markdown syntax is present (headers, lists, code blocks, links, bold/italic) ā no need to set explicitly. Can be overridden manually.</td>
<td valign="top">No</td>
<td valign="top">
<i>Auto-detection: no configuration needed.</i><br/><br/>
<details>
<summary>Manual override example</summary>
<pre><code>{
title: "Task Complete",
message: "Regular plain text message",
markdown: false // Force disable
}</code></pre>
</details>
</td>
</tr>
<tr>
<td valign="top"><code>actions</code></td>
<td valign="top">Array of view action objects for clickable links (at most 3 per notification). URLs in the message body are auto-detected (up to 3 actions). For manual control, each action requires <code>action</code>, <code>label</code>, and an <code>http://</code> or <code>https://</code> <code>url</code> without embedded credentials, with an optional <code>clear</code> flag.</td>
<td valign="top">No</td>
<td valign="top">
<details>
<summary>Auto-detection example</summary>
<pre><code>{
title: "Build Complete",
message: "View at https://github.com/org/repo/pull/123"
}</code></pre>
Automatically creates view actions for detected URLs.
</details>
<details>
<summary>Manual configuration example</summary>
<pre><code>{
title: "Pull Request Review",
message: "Ready for final checks",
actions: [
{
action: "view",
label: "View PR",
url: "https://github.com/org/repo/pull/123"
},
{
action: "view",
label: "View Changes",
url: "https://github.com/org/repo/pull/123/files",
clear: true
}
]
}</code></pre>
</details>
</td>
</tr>
<tr>
<td valign="top"><code>click</code></td>
<td valign="top">Link opened when the notification itself is tapped (ntfy's <a href="https://docs.ntfy.sh/publish/#click-action">click action</a>). Must be <code>http://</code>, <code>https://</code>, <code>mailto:</code>, <code>geo:</code> or <code>ntfy://</code>, without embedded credentials. A blank value counts as not provided</td>
<td valign="top">No</td>
<td valign="top"><code>{ "click": "https://github.com/user/repo/actions/runs/123" }</code></td>
</tr>
</tbody>
</table>
### `ntfy_me_fetch`: Polling Notifications
#### Using Natural Language
AI assistants understand various ways to request message fetching:
```
"Show me my recent notifications"
"Get messages from the last hour"
"Find notifications with title 'Build Complete'"
"Search for messages with the test_tube tag"
"Show notifications from the updates topic from the last 24hr" # needs updates in NTFY_TOPICS_ALLOWLIST (or NTFY_ALLOW_TOPIC_OVERRIDE=true)
"Check my latest alerts"
```
#### Example Usage
<table>
<thead>
<tr>
<th width="50%">Input</th>
<th width="50%">Output</th>
</tr>
</thead>
<tbody>
<tr>
<td valign="top"><pre><code>{
"since": "6h"
}</code></pre></td>
<td valign="top"><pre><code>{
"success": true,
"messageCount": 1,
"topics": {
"ntfymetest": [
{
"id": "On4Jeo1ENDCB",
"time": 1775859291,
"event": "message",
"topic": "ntfymetest",
"message": "Test",
"title": "Test",
"priority": 3,
"expires": 1775902491
}
]
}
}</code></pre></td>
</tr>
</tbody>
</table>
#### Fetch Parameters
<table>
<thead>
<tr>
<th>Parameter</th>
<th>Description</th>
<th>Required</th>
<th>Details / Example</th>
</tr>
</thead>
<tbody>
<tr>
<td valign="top"><code>url</code></td>
<td valign="top">Custom ntfy server URL. <b>Only offered when <code>NTFY_ALLOW_URL_OVERRIDE=true</code></b>. <code>NTFY_TOKEN</code> is only sent to the <code>NTFY_URL</code> server; pass <code>accessToken</code> for another server</td>
<td valign="top">No</td>
<td valign="top"><i>Default: <code>NTFY_URL</code></i></td>
</tr>
<tr>
<td valign="top"><code>topic</code></td>
<td valign="top">Topic to fetch messages from. <b>Only offered when <code>NTFY_TOPICS_ALLOWLIST</code> is set (limited to <code>NTFY_TOPIC</code> and those topics) or <code>NTFY_ALLOW_TOPIC_OVERRIDE=true</code></b></td>
<td valign="top">No</td>
<td valign="top"><i>Default: <code>NTFY_TOPIC</code></i><br/><br/><code>{ "topic": "updates", "since": "all" }</code></td>
</tr>
<tr>
<td valign="top"><code>accessToken</code></td>
<td valign="top">Access token for protected topics. A blank value (<code>""</code>, spaces) counts as not provided</td>
<td valign="top">No</td>
<td valign="top"><i>Default: <code>NTFY_TOKEN</code></i></td>
</tr>
<tr>
<td valign="top"><code>since</code></td>
<td valign="top">How far back to retrieve messages</td>
<td valign="top">No</td>
<td valign="top">
<i>Default: <code>"10m"</code></i><br/>
Options: <code>'10m'</code>, <code>'1h'</code>, <code>'1d'</code>, timestamp, message ID, or <code>'all'</code><br/>
Example: <code>{ "since": "30m" }</code>
</td>
</tr>
<tr>
<td valign="top"><code>messageId</code></td>
<td valign="top">Find a specific message by its ID</td>
<td valign="top">No</td>
<td valign="top"><code>{ "messageId": "xxxxXXXXxxxx" }</code></td>
</tr>
<tr>
<td valign="top"><code>messageText</code></td>
<td valign="top">Find messages containing exact text content</td>
<td valign="top">No</td>
<td valign="top"><code>{ "messageText": "Build Complete" }</code></td>
</tr>
<tr>
<td valign="top"><code>messageTitle</code></td>
<td valign="top">Find messages with exact title/subject</td>
<td valign="top">No</td>
<td valign="top"><code>{ "messageTitle": "Build Complete", "priorities": "high", "since": "1d" }</code></td>
</tr>
<tr>
<td valign="top"><code>priorities</code></td>
<td valign="top">Find messages with specific priority levels. A single value or an array.</td>
<td valign="top">No</td>
<td valign="top"><code>{ "priorities": "high" }</code><br/><code>{ "priorities": ["high", "max"] }</code></td>
</tr>
<tr>
<td valign="top"><code>tags</code></td>
<td valign="top">Find messages with specific tags. A single tag or an array; every listed tag must be present.</td>
<td valign="top">No</td>
<td valign="top"><code>{ "tags": "error" }</code><br/><code>{ "tags": ["error", "warning"] }</code></td>
</tr>
</tbody>
</table>
### Tool output
Both tools return human-readable `content` for the model and a machine-readable `structuredContent` object. For `ntfy_me_fetch`, `content` also includes the fetched messages as JSON. That text comes from whoever published to the topic, so treat it as untrusted (see [Security](#security)).
| Tool | Success (`structuredContent`) |
| --- | --- |
| `ntfy_me` | `{ "success": true, "endpoint": "https://ntfy.sh/your-topic" }` |
| `ntfy_me_fetch` | `{ "success": true, "messageCount": 1, "topics": { "your-topic": [ /* messages */ ] } }`<br/>When the server returns an empty response (no cached messages in the requested window): `{ "success": true, "topic": "your-topic", "messages": [] }`<br/>When the response contains no valid message records: `{ "success": true, "messageCount": 0, "topics": {} }` |
**Invalid arguments** (a missing `title`, a malformed `topic`, an unknown `priority`, ā¦) are rejected by the MCP SDK's schema validation before the tool runs. They come back with `isError: true` and a text-only `MCP error -32602: Input validation error: ā¦` message that names the field and the expected format, with no `structuredContent`.
**Failures inside the tool** set `isError: true` and return `{ "success": false, "error": "<message>" }`. The error message is specific for an invalid `url` or `topic`, an authentication failure (HTTP 401/403), a non-2xx status code (`... Status code: 500`), or a network failure, named with Node's error code: `... could not connect to the ntfy server (ECONNREFUSED)` when no connection was made, or `... the request to the ntfy server failed (ECONNRESET)` when it broke after connecting (the notification may have been delivered). Anything else is reported generically (`Failed to send ntfy notification` / `Failed to fetch ntfy messages`), so raw input or server text is never echoed back to the model. The full underlying error is written to the server's stderr log.
## Security
ntfy-me-mcp treats every tool argument as untrusted, because it comes from a model that may have read prompt-injected content.
- **URLs:** only `http://` and `https://` server URLs are accepted, from both the `url` argument and `NTFY_URL`. URLs with embedded credentials (`https://user:pass@ā¦`) are rejected; use `NTFY_TOKEN` or `accessToken`.
- **Startup checks:** a missing or invalid `NTFY_TOPIC`, or an invalid `NTFY_URL` (not http(s), or containing `user:pass@`), stops the server at startup. The error names the variable and is written to stderr, which your MCP client shows in its server log. The server checks these before logging `NTFY_URL`, so embedded credentials never reach the log.
- **Tokens and logs:** access tokens must be printable ASCII without spaces. Errors are written to stderr with bearer values and URL credentials redacted.
- **Topics:** letters, numbers, `_` and `-` only, up to 128 characters.
- **Error messages:** only a fixed set of known-safe messages reaches the model. Everything else is replaced with a generic message, so attacker-controlled text is never reflected back (see [#13](https://github.com/gitmotion/ntfy-me-mcp/issues/13)).
- **Fetched messages are untrusted input.** `ntfy_me_fetch` hands message content to the model, and anyone who knows a public topic's name can publish to it. On ntfy.sh, use a hard-to-guess topic name, or a protected topic with an access token.
- **Destination control:** by default, every notification and fetch goes to `NTFY_TOPIC` on `NTFY_URL`. The agent is never offered the `topic` or `url` parameters, so a prompt-injected call can't send your messages, or your topic name (which is effectively a password on public ntfy.sh), anywhere else (see [#21](https://github.com/gitmotion/ntfy-me-mcp/issues/21)).
- `NTFY_TOPICS_ALLOWLIST=alerts,builds` lets the agent choose among the topics you actually subscribe to, plus `NTFY_TOPIC`, and no other topic (see [#34](https://github.com/gitmotion/ntfy-me-mcp/issues/34)). Any other topic is rejected before a request is made, with no silent fallback. These topic names, **including `NTFY_TOPIC`**, appear in the tool schema, so the model and your MCP client can see them. Without an allowlist, the tool schema never contains a topic name. On public ntfy.sh, a topic name works like a password, so only use the allowlist with names you're comfortable sharing with them, or with protected topics.
- `NTFY_ALLOW_TOPIC_OVERRIDE=true` and `NTFY_ALLOW_URL_OVERRIDE=true` opt back in to any topic or server, independently. If `NTFY_TOPICS_ALLOWLIST` is also set, it wins over `NTFY_ALLOW_TOPIC_OVERRIDE`.
- Even with url overrides on, `NTFY_TOKEN` is only ever sent to the `NTFY_URL` server. Other servers get no credentials unless the call supplies its own `accessToken`.
- **Tokens:** prefer your client's secret input (e.g. VS Code `${input:ā¦}`) or an environment variable over hardcoding `NTFY_TOKEN` in a config file you share.
Found a vulnerability? Please report it privately through [GitHub security advisories](https://github.com/gitmotion/ntfy-me-mcp/security/advisories/new) rather than in a public issue.
## Development & Contributions
Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md), which include general guidelines, setup steps, etc.
## License
This project is licensed under the GNU General Public License v3.0 - see the [LICENSE](LICENSE) file for details.
---
Made with ā¤ļø by [gitmotion](https://github.com/gitmotion)
TDQS
Scored across 2 tools
The two tools have clearly distinct purposes: one sends notifications, the other retrieves cached messages. There is no overlap or ambiguity in their functions.
Both tools share the prefix 'ntfy_me_', but the first tool lacks a specific verb suffix (e.g., 'send') while the second has '_fetch'. The pattern is mostly consistent but has a minor deviation.
With only two tools, the set is minimal but appropriate for the narrow scope of sending and fetching notifications. It feels slightly thin but not unreasonable for a simple domain.
The tool set covers the basic operations of sending and retrieving notifications, but lacks additional expected functionality such as deleting or managing topics, leaving some gaps for full lifecycle management.