omni-notify-mcp 1.3.6 → 1.3.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/ui/server.js CHANGED
@@ -141,6 +141,7 @@ function mergePreservingSecrets(existing, update) {
141
141
  return merged;
142
142
  }
143
143
  const app = express();
144
+ app.use((req, _res, next) => { console.log(`[${new Date().toISOString()}] ${req.method} ${req.url} UA:${req.headers['user-agent']?.slice(0, 60)}`); next(); });
144
145
  app.use(express.json());
145
146
  app.use(express.static(PUBLIC_DIR));
146
147
  const ntfySubscribers = new Map();
@@ -165,9 +166,8 @@ function ntfyFanout(topic, message, title, priority, tags) {
165
166
  }
166
167
  }
167
168
  }
168
- // SSE subscribe — ntfy app connects here
169
- app.get("/ntfy/:topic/sse", (req, res) => {
170
- const { topic } = req.params;
169
+ function handleNtfySse(req, res) {
170
+ const topic = req.params.topic;
171
171
  res.setHeader("Content-Type", "text/event-stream");
172
172
  res.setHeader("Cache-Control", "no-cache");
173
173
  res.setHeader("Connection", "keep-alive");
@@ -184,14 +184,10 @@ app.get("/ntfy/:topic/sse", (req, res) => {
184
184
  catch {
185
185
  clearInterval(keepalive);
186
186
  } }, 30_000);
187
- req.on("close", () => {
188
- clearInterval(keepalive);
189
- ntfySubscribers.get(topic)?.delete(sub);
190
- });
191
- });
192
- // Also support ntfy's JSON stream endpoint
193
- app.get("/ntfy/:topic/json", (req, res) => {
194
- const { topic } = req.params;
187
+ req.on("close", () => { clearInterval(keepalive); ntfySubscribers.get(topic)?.delete(sub); });
188
+ }
189
+ function handleNtfyJson(req, res) {
190
+ const topic = req.params.topic;
195
191
  res.setHeader("Content-Type", "application/x-ndjson");
196
192
  res.setHeader("Cache-Control", "no-cache");
197
193
  res.setHeader("Connection", "keep-alive");
@@ -208,12 +204,61 @@ app.get("/ntfy/:topic/json", (req, res) => {
208
204
  clearInterval(keepalive);
209
205
  }
210
206
  }, 30_000);
211
- req.on("close", () => {
212
- clearInterval(keepalive);
213
- ntfySubscribers.get(topic)?.delete(sub);
214
- });
207
+ req.on("close", () => { clearInterval(keepalive); ntfySubscribers.get(topic)?.delete(sub); });
208
+ }
209
+ // ntfy health + info endpoints — app checks these before subscribing
210
+ app.get("/v1/health", (_req, res) => res.json({ healthy: true }));
211
+ app.get("/v1/info", (_req, res) => res.json({ version: "2.11.0", sha: "n/a" }));
212
+ // ntfy app hits /:topic/sse or /:topic/json (no /ntfy/ prefix)
213
+ app.get("/:topic/sse", (req, res) => {
214
+ if (["api", "auth", "mcp", "assets", "ntfy"].includes(req.params.topic)) {
215
+ res.status(404).end();
216
+ return;
217
+ }
218
+ handleNtfySse(req, res);
215
219
  });
216
- // Publish endpoint ntfy protocol POST
220
+ app.get("/:topic/json", (req, res) => {
221
+ if (["api", "auth", "mcp", "assets", "ntfy"].includes(req.params.topic)) {
222
+ res.status(404).end();
223
+ return;
224
+ }
225
+ handleNtfyJson(req, res);
226
+ });
227
+ // Also with /ntfy/ prefix for internal use
228
+ app.get("/ntfy/:topic/sse", (req, res) => handleNtfySse(req, res));
229
+ app.get("/ntfy/:topic/json", (req, res) => handleNtfyJson(req, res));
230
+ // Publish endpoint — ntfy protocol POST (with and without /ntfy/ prefix)
231
+ app.put("/:topic", express.text({ type: "*/*" }), (req, res, next) => {
232
+ if (["api", "auth", "mcp", "assets", "ntfy"].includes(req.params.topic)) {
233
+ next();
234
+ return;
235
+ }
236
+ handleNtfyPublish(req, res);
237
+ });
238
+ app.post("/:topic", express.text({ type: "*/*" }), (req, res, next) => {
239
+ if (["api", "auth", "mcp", "assets", "ntfy"].includes(req.params.topic)) {
240
+ next();
241
+ return;
242
+ }
243
+ handleNtfyPublish(req, res);
244
+ });
245
+ app.get("/:topic/subscribers", (req, res) => {
246
+ if (["api", "auth", "mcp", "assets", "ntfy"].includes(req.params.topic)) {
247
+ res.status(404).end();
248
+ return;
249
+ }
250
+ const count = ntfySubscribers.get(req.params.topic)?.size ?? 0;
251
+ res.json({ topic: req.params.topic, subscribers: count });
252
+ });
253
+ function handleNtfyPublish(req, res) {
254
+ const topic = req.params.topic;
255
+ const message = typeof req.body === "string" ? req.body : "";
256
+ const title = decodeURIComponent((req.headers["title"] || req.headers["x-title"] || "Claude Notify"));
257
+ const priority = parseInt((req.headers["priority"] || req.headers["x-priority"] || "3")) || 3;
258
+ const tags = (req.headers["tags"] || req.headers["x-tags"] || "");
259
+ ntfyFanout(topic, message, title, priority, tags);
260
+ res.json({ id: String(Date.now()), time: Math.floor(Date.now() / 1000), event: "message", topic, title, message, priority });
261
+ }
217
262
  app.put("/ntfy/:topic", express.text({ type: "*/*" }), (req, res) => {
218
263
  const { topic } = req.params;
219
264
  const message = typeof req.body === "string" ? req.body : "";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "omni-notify-mcp",
3
- "version": "1.3.6",
3
+ "version": "1.3.8",
4
4
  "description": "An MCP server that lets AI agents (Claude, Cursor, etc.) reach you on any channel — desktop, Telegram, SMS, email — with two-way ask/reply, real-time inbox push, Do Not Disturb, idle gating, multi-session routing, and a one-page web UI for setup. Zero config code; configure once, agents call notify/ask.",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
package/ui/public/app.js CHANGED
@@ -83,7 +83,7 @@ function populateForm() {
83
83
  $("ntfy-enabled").checked = !!ntfy.enabled;
84
84
  $("ntfy-topic").value = ntfy.topic ?? "";
85
85
  const defaultUrl = `${location.protocol}//${location.hostname}:${location.port || (location.protocol === 'https:' ? 443 : 80)}`;
86
- $("ntfy-server-url").value = ntfy.serverUrl || defaultUrl;
86
+ $("ntfy-server-url").value = (ntfy.serverUrl || defaultUrl).replace(/\/ntfy\/?$/, "");
87
87
 
88
88
  // Discord
89
89
  const dc = config.discord ?? {};