agent-notify-chime 0.1.0
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/LICENSE +21 -0
- package/README.md +158 -0
- package/index.js +306 -0
- package/package.json +42 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# Agent Notify Chime MCP Server
|
|
2
|
+
|
|
3
|
+
A lightweight Model Context Protocol (MCP) server that sends push notifications through [ntfy](https://ntfy.sh). It mirrors the feature set from `simple-ntfy-mcp` and adds clear setup guides for Codex, VS Code, and Windsurf clients.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Zero-setup usage via `npx` or local install
|
|
8
|
+
- Sends notifications to any ntfy topic
|
|
9
|
+
- Optional title, priority, tags, click actions, and action buttons
|
|
10
|
+
- Works with STDIO MCP clients (Codex CLI/IDE, VS Code extensions, Windsurf)
|
|
11
|
+
|
|
12
|
+
## Requirements
|
|
13
|
+
|
|
14
|
+
- Node.js 18+
|
|
15
|
+
- An ntfy topic name (and optional ntfy credentials if you self-host)
|
|
16
|
+
|
|
17
|
+
## Quickstart (local)
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install
|
|
21
|
+
npm run start
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
This runs the MCP server on stdio. Use the client configuration examples below to connect.
|
|
25
|
+
|
|
26
|
+
## Environment variables
|
|
27
|
+
|
|
28
|
+
| Variable | Description | Default |
|
|
29
|
+
| --- | --- | --- |
|
|
30
|
+
| `NTFY_DEFAULT_TOPIC` | Default topic to send notifications to | required |
|
|
31
|
+
| `NTFY_BASE_URL` | Base URL of the ntfy server | `https://ntfy.sh` |
|
|
32
|
+
| `NTFY_AUTH_TOKEN` | Bearer token for ntfy auth (optional) | empty |
|
|
33
|
+
| `NTFY_USERNAME` | Username for Basic auth (optional) | empty |
|
|
34
|
+
| `NTFY_PASSWORD` | Password for Basic auth (optional) | empty |
|
|
35
|
+
| `NTFY_REQUEST_TIMEOUT_MS` | Request timeout in ms | `10000` |
|
|
36
|
+
|
|
37
|
+
## Tool: `send_ntfy`
|
|
38
|
+
|
|
39
|
+
Send a push notification to your configured ntfy topic.
|
|
40
|
+
|
|
41
|
+
### Parameters
|
|
42
|
+
|
|
43
|
+
| Parameter | Type | Required | Description |
|
|
44
|
+
| --- | --- | --- | --- |
|
|
45
|
+
| `message` | string | yes | The notification message |
|
|
46
|
+
| `topic` | string | no | Topic to send to (uses default if not specified) |
|
|
47
|
+
| `title` | string | no | Notification title |
|
|
48
|
+
| `priority` | number | no | Priority level 1-5 (default 3) |
|
|
49
|
+
| `tags` | string[] | no | Array of tags/emojis |
|
|
50
|
+
| `click` | string | no | URL to open when clicked |
|
|
51
|
+
| `actions` | object[] | no | Action buttons per ntfy docs |
|
|
52
|
+
|
|
53
|
+
### Example tool call
|
|
54
|
+
|
|
55
|
+
```json
|
|
56
|
+
{
|
|
57
|
+
"message": "Build finished successfully",
|
|
58
|
+
"title": "CI",
|
|
59
|
+
"priority": 4,
|
|
60
|
+
"tags": ["white_check_mark", "rocket"],
|
|
61
|
+
"click": "https://example.com/builds/123"
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Client setup
|
|
66
|
+
|
|
67
|
+
Use the published package command below:
|
|
68
|
+
|
|
69
|
+
- **Published package**: `command = "npx"`, `args = ["-y", "agent-notify-chime"]`
|
|
70
|
+
|
|
71
|
+
### Codex (CLI + IDE extension)
|
|
72
|
+
|
|
73
|
+
Codex reads MCP servers from `~/.codex/config.toml` and shares this config with the IDE extension (VS Code, Cursor, Windsurf).
|
|
74
|
+
|
|
75
|
+
**Option A: CLI command**
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
codex mcp add agent-notify-chime \
|
|
79
|
+
--env NTFY_DEFAULT_TOPIC=your-topic \
|
|
80
|
+
-- npx -y agent-notify-chime
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
**Option B: Edit `config.toml`**
|
|
84
|
+
|
|
85
|
+
```toml
|
|
86
|
+
[mcp_servers.agent-notify-chime]
|
|
87
|
+
command = "npx"
|
|
88
|
+
args = ["-y", "agent-notify-chime"]
|
|
89
|
+
|
|
90
|
+
[mcp_servers.agent-notify-chime.env]
|
|
91
|
+
NTFY_DEFAULT_TOPIC = "your-topic"
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### VS Code (Cline extension)
|
|
95
|
+
|
|
96
|
+
Cline is a popular MCP client for VS Code. It stores MCP settings in `cline_mcp_settings.json` (open it via the MCP Servers panel).
|
|
97
|
+
|
|
98
|
+
```json
|
|
99
|
+
{
|
|
100
|
+
"mcpServers": {
|
|
101
|
+
"agent-notify-chime": {
|
|
102
|
+
"command": "npx",
|
|
103
|
+
"args": ["-y", "agent-notify-chime"],
|
|
104
|
+
"env": {
|
|
105
|
+
"NTFY_DEFAULT_TOPIC": "your-topic"
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
After editing, restart the server from the MCP Servers panel.
|
|
113
|
+
|
|
114
|
+
### Windsurf (Cascade)
|
|
115
|
+
|
|
116
|
+
Windsurf reads MCP settings from `~/.codeium/windsurf/mcp_config.json`.
|
|
117
|
+
|
|
118
|
+
```json
|
|
119
|
+
{
|
|
120
|
+
"mcpServers": {
|
|
121
|
+
"agent-notify-chime": {
|
|
122
|
+
"command": "npx",
|
|
123
|
+
"args": ["-y", "agent-notify-chime"],
|
|
124
|
+
"env": {
|
|
125
|
+
"NTFY_DEFAULT_TOPIC": "your-topic"
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Restart Cascade or toggle the server to apply changes.
|
|
133
|
+
|
|
134
|
+
## Implementation notes
|
|
135
|
+
|
|
136
|
+
- This server uses the MCP SDK over stdio and publishes a single tool: `send_ntfy`.
|
|
137
|
+
- Notifications are sent by POSTing to `NTFY_BASE_URL/<topic>` with ntfy headers.
|
|
138
|
+
- Auth is optional (Bearer token or Basic auth) for self-hosted ntfy servers.
|
|
139
|
+
|
|
140
|
+
## Future improvements (recommended)
|
|
141
|
+
|
|
142
|
+
- Add attachment support (files, images) via ntfy `X-Attach`.
|
|
143
|
+
- Add retry/backoff for transient HTTP failures.
|
|
144
|
+
- Provide a small CLI to validate config/env variables before launching.
|
|
145
|
+
- Support ntfy message templating (predefined titles/tags).
|
|
146
|
+
- Optional metrics or structured logging for observability.
|
|
147
|
+
|
|
148
|
+
## Publish to npm
|
|
149
|
+
|
|
150
|
+
1. Ensure you are logged in: `npm login`
|
|
151
|
+
2. Update the version if needed: `npm version patch` (or `minor`/`major`)
|
|
152
|
+
3. Publish: `npm publish --access public`
|
|
153
|
+
|
|
154
|
+
If you are publishing for the first time, make sure the package name `agent-notify-chime` is available on npm.
|
|
155
|
+
|
|
156
|
+
## License
|
|
157
|
+
|
|
158
|
+
MIT
|
package/index.js
ADDED
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
4
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
|
+
import {
|
|
6
|
+
CallToolRequestSchema,
|
|
7
|
+
ListToolsRequestSchema,
|
|
8
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
9
|
+
|
|
10
|
+
const NTFY_BASE_URL = (process.env.NTFY_BASE_URL || "https://ntfy.sh").replace(
|
|
11
|
+
/\/+$/,
|
|
12
|
+
""
|
|
13
|
+
);
|
|
14
|
+
const DEFAULT_TOPIC = process.env.NTFY_DEFAULT_TOPIC || "";
|
|
15
|
+
const AUTH_TOKEN = process.env.NTFY_AUTH_TOKEN || "";
|
|
16
|
+
const NTFY_USERNAME = process.env.NTFY_USERNAME || "";
|
|
17
|
+
const NTFY_PASSWORD = process.env.NTFY_PASSWORD || "";
|
|
18
|
+
const REQUEST_TIMEOUT_MS = Number.parseInt(
|
|
19
|
+
process.env.NTFY_REQUEST_TIMEOUT_MS || "10000",
|
|
20
|
+
10
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
const server = new Server(
|
|
24
|
+
{
|
|
25
|
+
name: "agent-notify-chime",
|
|
26
|
+
version: "0.1.0",
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
capabilities: {
|
|
30
|
+
tools: {},
|
|
31
|
+
},
|
|
32
|
+
}
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
function getAuthHeader() {
|
|
36
|
+
if (AUTH_TOKEN) {
|
|
37
|
+
return `Bearer ${AUTH_TOKEN}`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (NTFY_USERNAME && NTFY_PASSWORD) {
|
|
41
|
+
const encoded = Buffer.from(`${NTFY_USERNAME}:${NTFY_PASSWORD}`).toString(
|
|
42
|
+
"base64"
|
|
43
|
+
);
|
|
44
|
+
return `Basic ${encoded}`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function buildActionsHeader(actions) {
|
|
51
|
+
if (!Array.isArray(actions)) {
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const serialized = actions
|
|
56
|
+
.map((action) => {
|
|
57
|
+
if (!action || typeof action.action !== "string") {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const label = typeof action.label === "string" ? action.label : "";
|
|
62
|
+
const url = typeof action.url === "string" ? action.url : "";
|
|
63
|
+
const parts = [action.action, label, url];
|
|
64
|
+
|
|
65
|
+
if (typeof action.method === "string" && action.method) {
|
|
66
|
+
parts.push(`method=${action.method}`);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return parts.join(", ");
|
|
70
|
+
})
|
|
71
|
+
.filter(Boolean)
|
|
72
|
+
.join("; ");
|
|
73
|
+
|
|
74
|
+
return serialized || undefined;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function normalizeTags(tags) {
|
|
78
|
+
if (!Array.isArray(tags)) {
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const filtered = tags.filter((tag) => typeof tag === "string" && tag.trim());
|
|
83
|
+
return filtered.length > 0 ? filtered : undefined;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
87
|
+
return {
|
|
88
|
+
tools: [
|
|
89
|
+
{
|
|
90
|
+
name: "send_ntfy",
|
|
91
|
+
description: "Send a notification via ntfy",
|
|
92
|
+
inputSchema: {
|
|
93
|
+
type: "object",
|
|
94
|
+
properties: {
|
|
95
|
+
topic: {
|
|
96
|
+
type: "string",
|
|
97
|
+
description: "The ntfy topic to send to",
|
|
98
|
+
},
|
|
99
|
+
message: {
|
|
100
|
+
type: "string",
|
|
101
|
+
description: "The message to send",
|
|
102
|
+
},
|
|
103
|
+
title: {
|
|
104
|
+
type: "string",
|
|
105
|
+
description: "Optional notification title",
|
|
106
|
+
},
|
|
107
|
+
priority: {
|
|
108
|
+
type: "number",
|
|
109
|
+
description: "Priority level (1-5, default 3)",
|
|
110
|
+
minimum: 1,
|
|
111
|
+
maximum: 5,
|
|
112
|
+
},
|
|
113
|
+
tags: {
|
|
114
|
+
type: "array",
|
|
115
|
+
items: { type: "string" },
|
|
116
|
+
description: "Optional tags/emojis",
|
|
117
|
+
},
|
|
118
|
+
click: {
|
|
119
|
+
type: "string",
|
|
120
|
+
description: "URL to open when notification is clicked",
|
|
121
|
+
},
|
|
122
|
+
actions: {
|
|
123
|
+
type: "array",
|
|
124
|
+
description: "Action buttons",
|
|
125
|
+
items: {
|
|
126
|
+
type: "object",
|
|
127
|
+
properties: {
|
|
128
|
+
action: { type: "string" },
|
|
129
|
+
label: { type: "string" },
|
|
130
|
+
url: { type: "string" },
|
|
131
|
+
method: { type: "string" },
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
required: ["message"],
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
],
|
|
140
|
+
};
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
144
|
+
if (request.params.name !== "send_ntfy") {
|
|
145
|
+
throw new Error(`Unknown tool: ${request.params.name}`);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const args = request.params.arguments ?? {};
|
|
149
|
+
const message = typeof args.message === "string" ? args.message : "";
|
|
150
|
+
const topic =
|
|
151
|
+
typeof args.topic === "string" && args.topic.trim()
|
|
152
|
+
? args.topic.trim()
|
|
153
|
+
: DEFAULT_TOPIC;
|
|
154
|
+
|
|
155
|
+
if (!message.trim()) {
|
|
156
|
+
return {
|
|
157
|
+
content: [
|
|
158
|
+
{
|
|
159
|
+
type: "text",
|
|
160
|
+
text: JSON.stringify(
|
|
161
|
+
{
|
|
162
|
+
success: false,
|
|
163
|
+
error: "Message is required",
|
|
164
|
+
},
|
|
165
|
+
null,
|
|
166
|
+
2
|
|
167
|
+
),
|
|
168
|
+
},
|
|
169
|
+
],
|
|
170
|
+
isError: true,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (!topic) {
|
|
175
|
+
return {
|
|
176
|
+
content: [
|
|
177
|
+
{
|
|
178
|
+
type: "text",
|
|
179
|
+
text: JSON.stringify(
|
|
180
|
+
{
|
|
181
|
+
success: false,
|
|
182
|
+
error: "No topic specified and no default topic configured",
|
|
183
|
+
},
|
|
184
|
+
null,
|
|
185
|
+
2
|
|
186
|
+
),
|
|
187
|
+
},
|
|
188
|
+
],
|
|
189
|
+
isError: true,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const url = `${NTFY_BASE_URL}/${encodeURIComponent(topic)}`;
|
|
194
|
+
const headers = {
|
|
195
|
+
"Content-Type": "text/plain",
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
const authHeader = getAuthHeader();
|
|
199
|
+
if (authHeader) {
|
|
200
|
+
headers.Authorization = authHeader;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (typeof args.title === "string" && args.title) {
|
|
204
|
+
headers["X-Title"] = args.title;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (typeof args.priority === "number") {
|
|
208
|
+
headers["X-Priority"] = String(args.priority);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const tags = normalizeTags(args.tags);
|
|
212
|
+
if (tags) {
|
|
213
|
+
headers["X-Tags"] = tags.join(",");
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (typeof args.click === "string" && args.click) {
|
|
217
|
+
headers["X-Click"] = args.click;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const actionsHeader = buildActionsHeader(args.actions);
|
|
221
|
+
if (actionsHeader) {
|
|
222
|
+
headers["X-Actions"] = actionsHeader;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const timeoutMs = Number.isFinite(REQUEST_TIMEOUT_MS)
|
|
226
|
+
? REQUEST_TIMEOUT_MS
|
|
227
|
+
: 10000;
|
|
228
|
+
const controller = new AbortController();
|
|
229
|
+
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
230
|
+
|
|
231
|
+
try {
|
|
232
|
+
const response = await fetch(url, {
|
|
233
|
+
method: "POST",
|
|
234
|
+
headers,
|
|
235
|
+
body: message,
|
|
236
|
+
signal: controller.signal,
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
if (!response.ok) {
|
|
240
|
+
const responseText = await response.text().catch(() => "");
|
|
241
|
+
throw new Error(
|
|
242
|
+
`HTTP ${response.status}${responseText ? `: ${responseText}` : ""}`
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const contentType = response.headers.get("content-type") || "";
|
|
247
|
+
const result = contentType.includes("application/json")
|
|
248
|
+
? await response.json()
|
|
249
|
+
: { body: await response.text() };
|
|
250
|
+
|
|
251
|
+
const payload = {
|
|
252
|
+
success: true,
|
|
253
|
+
topic,
|
|
254
|
+
response: result,
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
if (result && typeof result === "object") {
|
|
258
|
+
if (result.id) payload.messageId = result.id;
|
|
259
|
+
if (result.time) payload.time = result.time;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
return {
|
|
263
|
+
content: [
|
|
264
|
+
{
|
|
265
|
+
type: "text",
|
|
266
|
+
text: JSON.stringify(payload, null, 2),
|
|
267
|
+
},
|
|
268
|
+
],
|
|
269
|
+
};
|
|
270
|
+
} catch (error) {
|
|
271
|
+
const messageText =
|
|
272
|
+
error && error.name === "AbortError"
|
|
273
|
+
? `Request timed out after ${timeoutMs}ms`
|
|
274
|
+
: error?.message || "Unknown error";
|
|
275
|
+
|
|
276
|
+
return {
|
|
277
|
+
content: [
|
|
278
|
+
{
|
|
279
|
+
type: "text",
|
|
280
|
+
text: JSON.stringify(
|
|
281
|
+
{
|
|
282
|
+
success: false,
|
|
283
|
+
error: messageText,
|
|
284
|
+
},
|
|
285
|
+
null,
|
|
286
|
+
2
|
|
287
|
+
),
|
|
288
|
+
},
|
|
289
|
+
],
|
|
290
|
+
isError: true,
|
|
291
|
+
};
|
|
292
|
+
} finally {
|
|
293
|
+
clearTimeout(timeoutId);
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
async function main() {
|
|
298
|
+
const transport = new StdioServerTransport();
|
|
299
|
+
await server.connect(transport);
|
|
300
|
+
console.error("Agent Notify Chime MCP Server running on stdio");
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
main().catch((error) => {
|
|
304
|
+
console.error("Fatal error:", error);
|
|
305
|
+
process.exit(1);
|
|
306
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "agent-notify-chime",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "MCP server for sending ntfy notifications",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"agent-notify-chime": "./index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"index.js",
|
|
11
|
+
"README.md",
|
|
12
|
+
"LICENSE"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"start": "node index.js"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"mcp",
|
|
19
|
+
"ntfy",
|
|
20
|
+
"notifications",
|
|
21
|
+
"model-context-protocol",
|
|
22
|
+
"codex",
|
|
23
|
+
"vscode",
|
|
24
|
+
"windsurf"
|
|
25
|
+
],
|
|
26
|
+
"author": "",
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "git+https://github.com/ace3/agent-notify-chime.git"
|
|
30
|
+
},
|
|
31
|
+
"bugs": {
|
|
32
|
+
"url": "https://github.com/ace3/agent-notify-chime/issues"
|
|
33
|
+
},
|
|
34
|
+
"homepage": "https://github.com/ace3/agent-notify-chime#readme",
|
|
35
|
+
"license": "MIT",
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@modelcontextprotocol/sdk": "^1.0.4"
|
|
38
|
+
},
|
|
39
|
+
"engines": {
|
|
40
|
+
"node": ">=18"
|
|
41
|
+
}
|
|
42
|
+
}
|