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 +61 -16
- package/package.json +1 -1
- package/ui/public/app.js +1 -1
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
|
-
|
|
169
|
-
|
|
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
|
-
|
|
189
|
-
|
|
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
|
-
|
|
213
|
-
|
|
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
|
-
|
|
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.
|
|
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 ?? {};
|