ntfy-mcp-server 1.0.1 → 1.0.2
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/README.md +1 -1
- package/dist/mcp-server/server.js +28 -20
- package/dist/mcp-server/tools/ntfyTool/index.js +29 -15
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.typescriptlang.org/)
|
|
4
4
|
[](https://modelcontextprotocol.io/)
|
|
5
|
-
[](https://github.com/cyanheads/ntfy-mcp-server/releases)
|
|
6
6
|
[](https://opensource.org/licenses/Apache-2.0)
|
|
7
7
|
[](https://github.com/cyanheads/ntfy-mcp-server)
|
|
8
8
|
[](https://github.com/cyanheads/ntfy-mcp-server)
|
|
@@ -23,14 +23,16 @@ const MAX_FILE_SIZE = 5 * 1024 * 1024;
|
|
|
23
23
|
/**
|
|
24
24
|
* Load package information directly from package.json
|
|
25
25
|
*
|
|
26
|
+
* @param logger - The logger instance to use for logging
|
|
26
27
|
* @returns A promise resolving to an object with the package name and version
|
|
27
28
|
*/
|
|
28
|
-
const loadPackageInfo = async () => {
|
|
29
|
+
const loadPackageInfo = async (loggerInstance) => {
|
|
30
|
+
const pkgLogger = loggerInstance || logger.createChildLogger({ module: 'PackageInfo' });
|
|
29
31
|
return await ErrorHandler.tryCatch(async () => {
|
|
30
32
|
// Use the globally defined __dirname from the top of the file
|
|
31
33
|
const pkgPath = path.resolve(__dirname, '../../package.json');
|
|
32
34
|
const safePath = sanitizeInput.path(pkgPath);
|
|
33
|
-
|
|
35
|
+
pkgLogger.debug(`Looking for package.json at: ${safePath}`);
|
|
34
36
|
// Get file stats to check size before reading
|
|
35
37
|
const stats = await fs.stat(safePath);
|
|
36
38
|
// Check file size to prevent DoS attacks
|
|
@@ -126,27 +128,25 @@ export const createMcpServer = async () => {
|
|
|
126
128
|
newState
|
|
127
129
|
});
|
|
128
130
|
});
|
|
129
|
-
console.error("Initializing MCP server...");
|
|
130
131
|
serverLogger.info("Initializing server...");
|
|
131
132
|
const timers = [];
|
|
132
133
|
return await ErrorHandler.tryCatch(async () => {
|
|
133
134
|
// Load package info asynchronously
|
|
134
|
-
const packageInfo = await loadPackageInfo();
|
|
135
|
+
const packageInfo = await loadPackageInfo(serverLogger);
|
|
135
136
|
// Update logger with package info
|
|
136
|
-
console.error("Loaded package info:", packageInfo.name, packageInfo.version);
|
|
137
137
|
serverLogger.info("Loaded package info", {
|
|
138
138
|
name: packageInfo.name,
|
|
139
139
|
version: packageInfo.version
|
|
140
140
|
});
|
|
141
141
|
// Create the MCP server instance
|
|
142
|
-
|
|
142
|
+
serverLogger.debug("Creating MCP server instance...");
|
|
143
143
|
server = new McpServer({
|
|
144
144
|
name: packageInfo.name,
|
|
145
145
|
version: packageInfo.version
|
|
146
146
|
});
|
|
147
|
-
|
|
147
|
+
serverLogger.debug("MCP server instance created");
|
|
148
148
|
const registerComponent = async (type, name, registerFn) => {
|
|
149
|
-
|
|
149
|
+
serverLogger.debug(`Registering ${type}: ${name}`);
|
|
150
150
|
try {
|
|
151
151
|
await ErrorHandler.tryCatch(async () => await registerFn(), {
|
|
152
152
|
operation: `Register${type === 'tool' ? 'Tool' : 'Resource'}`,
|
|
@@ -160,16 +160,16 @@ export const createMcpServer = async () => {
|
|
|
160
160
|
else {
|
|
161
161
|
serverState.registeredResources.add(name);
|
|
162
162
|
}
|
|
163
|
-
|
|
163
|
+
serverLogger.debug(`Successfully registered ${type}: ${name}`);
|
|
164
164
|
return { success: true, type, name };
|
|
165
165
|
}
|
|
166
166
|
catch (error) {
|
|
167
|
-
|
|
167
|
+
serverLogger.error(`Failed to register ${type}: ${name}`, { error });
|
|
168
168
|
return { success: false, type, name, error };
|
|
169
169
|
}
|
|
170
170
|
};
|
|
171
171
|
// Register components with proper error handling
|
|
172
|
-
|
|
172
|
+
serverLogger.debug("Registering components...");
|
|
173
173
|
const registrationPromises = [
|
|
174
174
|
registerComponent('tool', 'send_ntfy', () => registerNtfyTool(server)),
|
|
175
175
|
registerComponent('resource', 'ntfy-resource', () => registerNtfyResource(server)),
|
|
@@ -192,30 +192,32 @@ export const createMcpServer = async () => {
|
|
|
192
192
|
});
|
|
193
193
|
// Process failed registrations
|
|
194
194
|
if (failedRegistrations.length > 0) {
|
|
195
|
-
console.error(`${failedRegistrations.length} registrations failed initially`, failedRegistrations.map(f => `${f.type}:${f.name}`));
|
|
196
195
|
serverLogger.warn(`${failedRegistrations.length} registrations failed initially`, {
|
|
197
196
|
failedComponents: failedRegistrations.map(f => `${f.type}:${f.name}`)
|
|
198
197
|
});
|
|
199
198
|
}
|
|
200
199
|
// Add debug logs to diagnose the connection issue
|
|
201
|
-
|
|
200
|
+
serverLogger.debug("About to connect to stdio transport");
|
|
202
201
|
try {
|
|
203
202
|
// Connect using stdio transport
|
|
204
203
|
const transport = new StdioServerTransport();
|
|
205
|
-
|
|
204
|
+
serverLogger.debug("Created StdioServerTransport instance");
|
|
206
205
|
// Set event handlers - using type assertion to avoid TS errors
|
|
207
206
|
server.onerror = (err) => {
|
|
208
|
-
|
|
207
|
+
serverLogger.error(`Server error: ${err.message}`, { stack: err.stack });
|
|
209
208
|
};
|
|
210
209
|
// Skip setting onrequest since we don't have access to the type
|
|
211
210
|
await server.connect(transport);
|
|
212
|
-
|
|
211
|
+
serverLogger.debug("Connected to transport successfully");
|
|
213
212
|
}
|
|
214
213
|
catch (error) {
|
|
215
|
-
|
|
214
|
+
serverLogger.error("Error connecting to transport", {
|
|
215
|
+
error: error instanceof Error ? error.message : String(error),
|
|
216
|
+
stack: error instanceof Error ? error.stack : undefined
|
|
217
|
+
});
|
|
216
218
|
throw error;
|
|
217
219
|
}
|
|
218
|
-
|
|
220
|
+
serverLogger.info("MCP server initialized and connected");
|
|
219
221
|
return server;
|
|
220
222
|
}, {
|
|
221
223
|
operation: 'CreateMcpServer',
|
|
@@ -228,7 +230,10 @@ export const createMcpServer = async () => {
|
|
|
228
230
|
registeredResources: Array.from(serverState.registeredResources)
|
|
229
231
|
})
|
|
230
232
|
}).catch((error) => {
|
|
231
|
-
|
|
233
|
+
serverLogger.error("Fatal error in MCP server creation", {
|
|
234
|
+
error: error instanceof Error ? error.message : String(error),
|
|
235
|
+
stack: error instanceof Error ? error.stack : undefined
|
|
236
|
+
});
|
|
232
237
|
// Attempt to close server
|
|
233
238
|
if (server) {
|
|
234
239
|
try {
|
|
@@ -236,7 +241,10 @@ export const createMcpServer = async () => {
|
|
|
236
241
|
}
|
|
237
242
|
catch (closeError) {
|
|
238
243
|
// Already in error state, just log
|
|
239
|
-
|
|
244
|
+
serverLogger.error("Error while closing server during error recovery", {
|
|
245
|
+
error: closeError instanceof Error ? closeError.message : String(closeError),
|
|
246
|
+
stack: closeError instanceof Error ? closeError.stack : undefined
|
|
247
|
+
});
|
|
240
248
|
}
|
|
241
249
|
}
|
|
242
250
|
// Re-throw to communicate error to caller
|
|
@@ -4,7 +4,6 @@ import { ErrorHandler } from "../../../utils/errorHandler.js";
|
|
|
4
4
|
import { logger } from "../../../utils/logger.js";
|
|
5
5
|
import { createRequestContext } from "../../../utils/requestContext.js";
|
|
6
6
|
import { sanitizeInputForLogging } from "../../../utils/sanitization.js";
|
|
7
|
-
import { registerTool } from "../../utils/registrationHelper.js";
|
|
8
7
|
import { processNtfyMessage } from "./ntfyMessage.js";
|
|
9
8
|
import { SendNtfyToolInputSchema } from "./types.js";
|
|
10
9
|
// Create module logger
|
|
@@ -26,20 +25,29 @@ export const registerNtfyTool = async (server) => {
|
|
|
26
25
|
operation: 'registerNtfyTool',
|
|
27
26
|
component: 'NtfyTool'
|
|
28
27
|
});
|
|
28
|
+
// Create a tool-specific logger
|
|
29
|
+
const toolLogger = logger.createChildLogger({
|
|
30
|
+
module: 'NtfyTool',
|
|
31
|
+
operation: 'registration'
|
|
32
|
+
});
|
|
29
33
|
moduleLogger.info('Starting ntfy tool registration');
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
//
|
|
42
|
-
|
|
34
|
+
// Create a fresh schema with the latest config values
|
|
35
|
+
// This ensures we have the most up-to-date environment variables
|
|
36
|
+
const schemaWithLatestConfig = SendNtfyToolInputSchema();
|
|
37
|
+
// Log default topic info at registration time for verification
|
|
38
|
+
const ntfyConfig = config.ntfy;
|
|
39
|
+
toolLogger.info('Registering ntfy tool handler with config', {
|
|
40
|
+
defaultTopic: ntfyConfig.defaultTopic || '(not set)',
|
|
41
|
+
baseUrl: ntfyConfig.baseUrl,
|
|
42
|
+
apiKeyPresent: !!ntfyConfig.apiKey
|
|
43
|
+
});
|
|
44
|
+
try {
|
|
45
|
+
// Prepare the description with the default topic information
|
|
46
|
+
const defaultTopicInfo = ntfyConfig.defaultTopic
|
|
47
|
+
? `Default topic: "${ntfyConfig.defaultTopic}"`
|
|
48
|
+
: "No default topic configured";
|
|
49
|
+
// Register the tool directly using the SDK pattern
|
|
50
|
+
server.tool("send_ntfy", `Send notifications to the user's devices using ntfy.sh service with support for titles, priorities, tags, attachments, and actions. Use this tool to externally notify the user of something important. ${defaultTopicInfo}.`, schemaWithLatestConfig.shape, async (params) => {
|
|
43
51
|
// Create request context for tracking this invocation
|
|
44
52
|
const toolRequestCtx = createRequestContext({
|
|
45
53
|
operation: 'handleNtfyTool',
|
|
@@ -106,5 +114,11 @@ export const registerNtfyTool = async (server) => {
|
|
|
106
114
|
});
|
|
107
115
|
});
|
|
108
116
|
toolLogger.info("Ntfy tool handler registered successfully");
|
|
109
|
-
}
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
toolLogger.error("Failed to register ntfy tool", {
|
|
120
|
+
error: error instanceof Error ? error.message : String(error)
|
|
121
|
+
});
|
|
122
|
+
throw error; // Re-throw to propagate the error
|
|
123
|
+
}
|
|
110
124
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ntfy-mcp-server",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "An MCP (Model Context Protocol) server designed to interact with the ntfy push notification service. It enables LLMs and AI agents to send notifications to your devices with extensive customization options.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|