ntfy-mcp-server 1.0.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.
Files changed (59) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +423 -0
  3. package/dist/config/index.d.ts +23 -0
  4. package/dist/config/index.js +111 -0
  5. package/dist/index.d.ts +2 -0
  6. package/dist/index.js +108 -0
  7. package/dist/mcp-server/resources/ntfyResource/getNtfyTopic.d.ts +2 -0
  8. package/dist/mcp-server/resources/ntfyResource/getNtfyTopic.js +111 -0
  9. package/dist/mcp-server/resources/ntfyResource/index.d.ts +12 -0
  10. package/dist/mcp-server/resources/ntfyResource/index.js +72 -0
  11. package/dist/mcp-server/resources/ntfyResource/types.d.ts +27 -0
  12. package/dist/mcp-server/resources/ntfyResource/types.js +8 -0
  13. package/dist/mcp-server/server.d.ts +40 -0
  14. package/dist/mcp-server/server.js +245 -0
  15. package/dist/mcp-server/tools/ntfyTool/index.d.ts +11 -0
  16. package/dist/mcp-server/tools/ntfyTool/index.js +110 -0
  17. package/dist/mcp-server/tools/ntfyTool/ntfyMessage.d.ts +9 -0
  18. package/dist/mcp-server/tools/ntfyTool/ntfyMessage.js +289 -0
  19. package/dist/mcp-server/tools/ntfyTool/types.d.ts +252 -0
  20. package/dist/mcp-server/tools/ntfyTool/types.js +144 -0
  21. package/dist/mcp-server/utils/registrationHelper.d.ts +48 -0
  22. package/dist/mcp-server/utils/registrationHelper.js +63 -0
  23. package/dist/services/ntfy/constants.d.ts +37 -0
  24. package/dist/services/ntfy/constants.js +37 -0
  25. package/dist/services/ntfy/errors.d.ts +79 -0
  26. package/dist/services/ntfy/errors.js +134 -0
  27. package/dist/services/ntfy/index.d.ts +33 -0
  28. package/dist/services/ntfy/index.js +56 -0
  29. package/dist/services/ntfy/publisher.d.ts +66 -0
  30. package/dist/services/ntfy/publisher.js +229 -0
  31. package/dist/services/ntfy/subscriber.d.ts +81 -0
  32. package/dist/services/ntfy/subscriber.js +502 -0
  33. package/dist/services/ntfy/types.d.ts +161 -0
  34. package/dist/services/ntfy/types.js +4 -0
  35. package/dist/services/ntfy/utils.d.ts +85 -0
  36. package/dist/services/ntfy/utils.js +410 -0
  37. package/dist/types-global/errors.d.ts +35 -0
  38. package/dist/types-global/errors.js +39 -0
  39. package/dist/types-global/mcp.d.ts +30 -0
  40. package/dist/types-global/mcp.js +25 -0
  41. package/dist/types-global/tool.d.ts +61 -0
  42. package/dist/types-global/tool.js +99 -0
  43. package/dist/utils/errorHandler.d.ts +98 -0
  44. package/dist/utils/errorHandler.js +271 -0
  45. package/dist/utils/idGenerator.d.ts +94 -0
  46. package/dist/utils/idGenerator.js +149 -0
  47. package/dist/utils/index.d.ts +13 -0
  48. package/dist/utils/index.js +16 -0
  49. package/dist/utils/logger.d.ts +36 -0
  50. package/dist/utils/logger.js +92 -0
  51. package/dist/utils/rateLimiter.d.ts +115 -0
  52. package/dist/utils/rateLimiter.js +180 -0
  53. package/dist/utils/requestContext.d.ts +68 -0
  54. package/dist/utils/requestContext.js +91 -0
  55. package/dist/utils/sanitization.d.ts +224 -0
  56. package/dist/utils/sanitization.js +367 -0
  57. package/dist/utils/security.d.ts +26 -0
  58. package/dist/utils/security.js +27 -0
  59. package/package.json +47 -0
@@ -0,0 +1,252 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * Valid priority levels for ntfy messages
4
+ */
5
+ export declare const NTFY_PRIORITIES: readonly [1, 2, 3, 4, 5];
6
+ /**
7
+ * Creates a Zod schema for the send_ntfy tool with current environment values
8
+ * This function should be called at registration time to ensure it has the
9
+ * latest configuration values from the environment
10
+ *
11
+ * @returns A Zod schema for the ntfy tool
12
+ */
13
+ export declare function createSendNtfyToolSchema(): z.ZodObject<{
14
+ topic: z.ZodEffects<z.ZodString, string, string>;
15
+ message: z.ZodString;
16
+ title: z.ZodOptional<z.ZodString>;
17
+ tags: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
18
+ priority: z.ZodOptional<z.ZodNumber>;
19
+ click: z.ZodOptional<z.ZodString>;
20
+ actions: z.ZodOptional<z.ZodArray<z.ZodObject<{
21
+ id: z.ZodString;
22
+ label: z.ZodString;
23
+ action: z.ZodString;
24
+ url: z.ZodOptional<z.ZodString>;
25
+ method: z.ZodOptional<z.ZodString>;
26
+ headers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
27
+ body: z.ZodOptional<z.ZodString>;
28
+ clear: z.ZodOptional<z.ZodBoolean>;
29
+ }, "strip", z.ZodTypeAny, {
30
+ id: string;
31
+ label: string;
32
+ action: string;
33
+ method?: string | undefined;
34
+ url?: string | undefined;
35
+ headers?: Record<string, string> | undefined;
36
+ body?: string | undefined;
37
+ clear?: boolean | undefined;
38
+ }, {
39
+ id: string;
40
+ label: string;
41
+ action: string;
42
+ method?: string | undefined;
43
+ url?: string | undefined;
44
+ headers?: Record<string, string> | undefined;
45
+ body?: string | undefined;
46
+ clear?: boolean | undefined;
47
+ }>, "many">>;
48
+ attachment: z.ZodOptional<z.ZodObject<{
49
+ url: z.ZodString;
50
+ name: z.ZodOptional<z.ZodString>;
51
+ }, "strip", z.ZodTypeAny, {
52
+ url: string;
53
+ name?: string | undefined;
54
+ }, {
55
+ url: string;
56
+ name?: string | undefined;
57
+ }>>;
58
+ email: z.ZodOptional<z.ZodString>;
59
+ delay: z.ZodOptional<z.ZodString>;
60
+ cache: z.ZodOptional<z.ZodString>;
61
+ firebase: z.ZodOptional<z.ZodString>;
62
+ id: z.ZodOptional<z.ZodString>;
63
+ expires: z.ZodOptional<z.ZodString>;
64
+ markdown: z.ZodOptional<z.ZodBoolean>;
65
+ baseUrl: z.ZodOptional<z.ZodString>;
66
+ }, "strip", z.ZodTypeAny, {
67
+ message: string;
68
+ topic: string;
69
+ title?: string | undefined;
70
+ id?: string | undefined;
71
+ priority?: number | undefined;
72
+ tags?: string[] | undefined;
73
+ baseUrl?: string | undefined;
74
+ click?: string | undefined;
75
+ actions?: {
76
+ id: string;
77
+ label: string;
78
+ action: string;
79
+ method?: string | undefined;
80
+ url?: string | undefined;
81
+ headers?: Record<string, string> | undefined;
82
+ body?: string | undefined;
83
+ clear?: boolean | undefined;
84
+ }[] | undefined;
85
+ attachment?: {
86
+ url: string;
87
+ name?: string | undefined;
88
+ } | undefined;
89
+ email?: string | undefined;
90
+ delay?: string | undefined;
91
+ cache?: string | undefined;
92
+ firebase?: string | undefined;
93
+ expires?: string | undefined;
94
+ markdown?: boolean | undefined;
95
+ }, {
96
+ message: string;
97
+ topic: string;
98
+ title?: string | undefined;
99
+ id?: string | undefined;
100
+ priority?: number | undefined;
101
+ tags?: string[] | undefined;
102
+ baseUrl?: string | undefined;
103
+ click?: string | undefined;
104
+ actions?: {
105
+ id: string;
106
+ label: string;
107
+ action: string;
108
+ method?: string | undefined;
109
+ url?: string | undefined;
110
+ headers?: Record<string, string> | undefined;
111
+ body?: string | undefined;
112
+ clear?: boolean | undefined;
113
+ }[] | undefined;
114
+ attachment?: {
115
+ url: string;
116
+ name?: string | undefined;
117
+ } | undefined;
118
+ email?: string | undefined;
119
+ delay?: string | undefined;
120
+ cache?: string | undefined;
121
+ firebase?: string | undefined;
122
+ expires?: string | undefined;
123
+ markdown?: boolean | undefined;
124
+ }>;
125
+ export declare const SendNtfyToolInputSchema: () => z.ZodObject<{
126
+ topic: z.ZodEffects<z.ZodString, string, string>;
127
+ message: z.ZodString;
128
+ title: z.ZodOptional<z.ZodString>;
129
+ tags: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
130
+ priority: z.ZodOptional<z.ZodNumber>;
131
+ click: z.ZodOptional<z.ZodString>;
132
+ actions: z.ZodOptional<z.ZodArray<z.ZodObject<{
133
+ id: z.ZodString;
134
+ label: z.ZodString;
135
+ action: z.ZodString;
136
+ url: z.ZodOptional<z.ZodString>;
137
+ method: z.ZodOptional<z.ZodString>;
138
+ headers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
139
+ body: z.ZodOptional<z.ZodString>;
140
+ clear: z.ZodOptional<z.ZodBoolean>;
141
+ }, "strip", z.ZodTypeAny, {
142
+ id: string;
143
+ label: string;
144
+ action: string;
145
+ method?: string | undefined;
146
+ url?: string | undefined;
147
+ headers?: Record<string, string> | undefined;
148
+ body?: string | undefined;
149
+ clear?: boolean | undefined;
150
+ }, {
151
+ id: string;
152
+ label: string;
153
+ action: string;
154
+ method?: string | undefined;
155
+ url?: string | undefined;
156
+ headers?: Record<string, string> | undefined;
157
+ body?: string | undefined;
158
+ clear?: boolean | undefined;
159
+ }>, "many">>;
160
+ attachment: z.ZodOptional<z.ZodObject<{
161
+ url: z.ZodString;
162
+ name: z.ZodOptional<z.ZodString>;
163
+ }, "strip", z.ZodTypeAny, {
164
+ url: string;
165
+ name?: string | undefined;
166
+ }, {
167
+ url: string;
168
+ name?: string | undefined;
169
+ }>>;
170
+ email: z.ZodOptional<z.ZodString>;
171
+ delay: z.ZodOptional<z.ZodString>;
172
+ cache: z.ZodOptional<z.ZodString>;
173
+ firebase: z.ZodOptional<z.ZodString>;
174
+ id: z.ZodOptional<z.ZodString>;
175
+ expires: z.ZodOptional<z.ZodString>;
176
+ markdown: z.ZodOptional<z.ZodBoolean>;
177
+ baseUrl: z.ZodOptional<z.ZodString>;
178
+ }, "strip", z.ZodTypeAny, {
179
+ message: string;
180
+ topic: string;
181
+ title?: string | undefined;
182
+ id?: string | undefined;
183
+ priority?: number | undefined;
184
+ tags?: string[] | undefined;
185
+ baseUrl?: string | undefined;
186
+ click?: string | undefined;
187
+ actions?: {
188
+ id: string;
189
+ label: string;
190
+ action: string;
191
+ method?: string | undefined;
192
+ url?: string | undefined;
193
+ headers?: Record<string, string> | undefined;
194
+ body?: string | undefined;
195
+ clear?: boolean | undefined;
196
+ }[] | undefined;
197
+ attachment?: {
198
+ url: string;
199
+ name?: string | undefined;
200
+ } | undefined;
201
+ email?: string | undefined;
202
+ delay?: string | undefined;
203
+ cache?: string | undefined;
204
+ firebase?: string | undefined;
205
+ expires?: string | undefined;
206
+ markdown?: boolean | undefined;
207
+ }, {
208
+ message: string;
209
+ topic: string;
210
+ title?: string | undefined;
211
+ id?: string | undefined;
212
+ priority?: number | undefined;
213
+ tags?: string[] | undefined;
214
+ baseUrl?: string | undefined;
215
+ click?: string | undefined;
216
+ actions?: {
217
+ id: string;
218
+ label: string;
219
+ action: string;
220
+ method?: string | undefined;
221
+ url?: string | undefined;
222
+ headers?: Record<string, string> | undefined;
223
+ body?: string | undefined;
224
+ clear?: boolean | undefined;
225
+ }[] | undefined;
226
+ attachment?: {
227
+ url: string;
228
+ name?: string | undefined;
229
+ } | undefined;
230
+ email?: string | undefined;
231
+ delay?: string | undefined;
232
+ cache?: string | undefined;
233
+ firebase?: string | undefined;
234
+ expires?: string | undefined;
235
+ markdown?: boolean | undefined;
236
+ }>;
237
+ export type SendNtfyToolInput = z.infer<ReturnType<typeof createSendNtfyToolSchema>>;
238
+ /**
239
+ * Response structure for the send_ntfy tool
240
+ */
241
+ export interface SendNtfyToolResponse {
242
+ success: boolean;
243
+ id: string;
244
+ topic: string;
245
+ time: number;
246
+ expires?: number;
247
+ message: string;
248
+ title?: string;
249
+ url?: string;
250
+ /** Number of retries needed (if any) */
251
+ retries?: number;
252
+ }
@@ -0,0 +1,144 @@
1
+ import { z } from 'zod';
2
+ import { config } from '../../../config/index.js';
3
+ import { logger } from '../../../utils/logger.js';
4
+ import { createRequestContext } from '../../../utils/requestContext.js';
5
+ // Create a module-specific logger
6
+ const schemaLogger = logger.createChildLogger({
7
+ module: 'NtfyToolSchema'
8
+ });
9
+ /**
10
+ * Valid priority levels for ntfy messages
11
+ */
12
+ export const NTFY_PRIORITIES = [1, 2, 3, 4, 5];
13
+ /**
14
+ * Validates a ntfy topic string format
15
+ *
16
+ * @param topic - The topic string to validate
17
+ * @returns boolean indicating if topic is valid
18
+ */
19
+ function isValidTopic(topic) {
20
+ if (!topic)
21
+ return false;
22
+ return topic.trim() !== '' && !/[\r\n]/.test(topic);
23
+ }
24
+ /**
25
+ * Creates a Zod schema for the send_ntfy tool with current environment values
26
+ * This function should be called at registration time to ensure it has the
27
+ * latest configuration values from the environment
28
+ *
29
+ * @returns A Zod schema for the ntfy tool
30
+ */
31
+ export function createSendNtfyToolSchema() {
32
+ // Create request context for tracking
33
+ const requestCtx = createRequestContext({
34
+ operation: 'createSendNtfyToolSchema'
35
+ });
36
+ schemaLogger.debug('Creating send_ntfy tool schema');
37
+ // Get the latest configuration
38
+ const ntfyConfig = config.ntfy;
39
+ // Process configuration values
40
+ const baseUrl = ntfyConfig.baseUrl || 'https://ntfy.sh';
41
+ const defaultTopic = ntfyConfig.defaultTopic || '';
42
+ const maxMessageSize = ntfyConfig.maxMessageSize || 4096;
43
+ // Log the loaded config values for debugging
44
+ schemaLogger.debug('Loaded ntfy configuration', {
45
+ defaultTopic: defaultTopic || '(not set)',
46
+ baseUrl,
47
+ maxMessageSize
48
+ });
49
+ // Generate better description text based on current config
50
+ const topicDesc = defaultTopic
51
+ ? `The ntfy topic to send the notification to (required). Default topic configured: "${defaultTopic}". Use ntfy://default resource to identify the configured topic.`
52
+ : `The ntfy topic to send the notification to (required). No default topic configured. Use ntfy://default resource to check if a topic has been configured.`;
53
+ schemaLogger.debug('Schema configuration loaded', {
54
+ hasBaseUrl: !!baseUrl,
55
+ hasDefaultTopic: !!defaultTopic,
56
+ hasApiKey: !!ntfyConfig.apiKey,
57
+ maxMessageSize
58
+ });
59
+ // Create schema with the latest config values
60
+ const schema = z.object({
61
+ // Required parameters
62
+ topic: z.string()
63
+ .min(1, "Topic must not be empty")
64
+ .refine(isValidTopic, "Topic must not contain newlines")
65
+ .describe(topicDesc),
66
+ message: z.string()
67
+ .min(1, "Message must not be empty")
68
+ .max(maxMessageSize, `Message size cannot exceed ${maxMessageSize} bytes`)
69
+ .describe(`The message to send (notification body, max ${maxMessageSize} bytes)`),
70
+ // Optional parameters with improved descriptions
71
+ title: z.string()
72
+ .max(250, "Title should be under 250 characters")
73
+ .optional()
74
+ .describe('Message title (optional)'),
75
+ tags: z.array(z.string())
76
+ .max(5, "Maximum of 5 tags allowed")
77
+ .optional()
78
+ .describe('Tags that show as emojis (e.g., ["warning", "skull", "robot"])'),
79
+ priority: z.number()
80
+ .int()
81
+ .min(1)
82
+ .max(5)
83
+ .optional()
84
+ .describe('Message priority: 1=min, 2=low, 3=default, 4=high, 5=max'),
85
+ click: z.string()
86
+ .url("Must be a valid URL")
87
+ .optional()
88
+ .describe('URL to open when notification is clicked'),
89
+ actions: z.array(z.object({
90
+ id: z.string().describe('Action identifier'),
91
+ label: z.string().describe('Label for the action button'),
92
+ action: z.string().describe('Action type (e.g., view, broadcast, http)'),
93
+ url: z.string().url("Must be a valid URL").optional().describe('URL or data for the action'),
94
+ method: z.string().optional().describe('HTTP method for http actions'),
95
+ headers: z.record(z.string()).optional().describe('Additional headers for http actions'),
96
+ body: z.string().optional().describe('Body for http actions'),
97
+ clear: z.boolean().optional().describe('Clear notification after action (Default: false)')
98
+ }))
99
+ .max(3, "Maximum of 3 actions supported")
100
+ .optional()
101
+ .describe('Action buttons in the notification (max 3)'),
102
+ attachment: z.object({
103
+ url: z.string().url("Must be a valid URL").describe('URL of the attachment'),
104
+ name: z.string().optional().describe('Name of the attachment'),
105
+ })
106
+ .optional()
107
+ .describe('Attachment for the notification'),
108
+ email: z.string()
109
+ .email("Must be a valid email address")
110
+ .optional()
111
+ .describe('Email address to send the notification to'),
112
+ delay: z.string()
113
+ .optional()
114
+ .describe('Delay the message (e.g., 30m, 1h, tomorrow)'),
115
+ cache: z.string()
116
+ .optional()
117
+ .describe('Cache duration (e.g., 10m, 1h, 1d)'),
118
+ firebase: z.string()
119
+ .optional()
120
+ .describe('Firebase Cloud Messaging (FCM) topic to forward to'),
121
+ id: z.string()
122
+ .optional()
123
+ .describe('Unique ID for the message'),
124
+ expires: z.string()
125
+ .optional()
126
+ .describe('Message expiration (e.g., 10m, 1h, 1d)'),
127
+ markdown: z.boolean()
128
+ .optional()
129
+ .describe('Format message as markdown'),
130
+ // Server options
131
+ baseUrl: z.string()
132
+ .url("Must be a valid URL")
133
+ .optional()
134
+ .describe(`Base URL for the ntfy server (default: ${baseUrl})`)
135
+ // Authentication is handled automatically using API key from .env when available
136
+ });
137
+ schemaLogger.info('Send_ntfy tool schema created successfully', {
138
+ fieldCount: Object.keys(schema.shape).length
139
+ });
140
+ return schema;
141
+ }
142
+ // Create a dynamic version of the schema for type inference
143
+ // We want to ensure this is always recreated at runtime with the latest env values
144
+ export const SendNtfyToolInputSchema = () => createSendNtfyToolSchema();
@@ -0,0 +1,48 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import { ChildLogger } from "../../utils/logger.js";
3
+ /**
4
+ * Base interface for registration options
5
+ */
6
+ export interface RegistrationOptions {
7
+ /** Name of the component being registered */
8
+ name: string;
9
+ /** Logger context for creating a child logger */
10
+ loggerContext?: Record<string, unknown>;
11
+ }
12
+ /**
13
+ * Full options with component type
14
+ */
15
+ interface InternalRegistrationOptions extends RegistrationOptions {
16
+ /** Type of component (tool, resource, etc.) */
17
+ type: string;
18
+ }
19
+ /**
20
+ * Helper for consistent registration pattern with proper error handling
21
+ * @param server MCP server instance
22
+ * @param options Registration options
23
+ * @param registerFn Function that performs the actual registration
24
+ * @returns Promise resolving when registration is complete
25
+ */
26
+ export declare function registerComponent(server: McpServer, options: InternalRegistrationOptions, registerFn: (server: McpServer, childLogger: ChildLogger) => Promise<void>): Promise<void>;
27
+ /**
28
+ * Register a tool with the MCP server using a consistent pattern
29
+ * @param server MCP server instance
30
+ * @param options Tool registration options
31
+ * @param handlerFn Function that sets up the tool handler
32
+ * @returns Promise resolving when registration is complete
33
+ */
34
+ export declare function registerTool(server: McpServer, options: RegistrationOptions, handlerFn: (server: McpServer, logger: ChildLogger) => Promise<void>): Promise<void>;
35
+ /**
36
+ * Register a resource with the MCP server using a consistent pattern
37
+ * @param server MCP server instance
38
+ * @param options Resource registration options
39
+ * @param handlerFn Function that sets up the resource handler
40
+ * @returns Promise resolving when registration is complete
41
+ */
42
+ export declare function registerResource(server: McpServer, options: RegistrationOptions, handlerFn: (server: McpServer, logger: ChildLogger) => Promise<void>): Promise<void>;
43
+ declare const _default: {
44
+ registerComponent: typeof registerComponent;
45
+ registerTool: typeof registerTool;
46
+ registerResource: typeof registerResource;
47
+ };
48
+ export default _default;
@@ -0,0 +1,63 @@
1
+ import { BaseErrorCode, McpError } from "../../types-global/errors.js";
2
+ import { ErrorHandler } from "../../utils/errorHandler.js";
3
+ import { logger } from "../../utils/logger.js";
4
+ /**
5
+ * Helper for consistent registration pattern with proper error handling
6
+ * @param server MCP server instance
7
+ * @param options Registration options
8
+ * @param registerFn Function that performs the actual registration
9
+ * @returns Promise resolving when registration is complete
10
+ */
11
+ export async function registerComponent(server, options, registerFn) {
12
+ // Create a component-specific logger
13
+ const componentLogger = logger.createChildLogger({
14
+ module: `${options.type}Registration`,
15
+ componentName: options.name,
16
+ ...options.loggerContext
17
+ });
18
+ componentLogger.info(`Registering ${options.type}: ${options.name}`);
19
+ // Use ErrorHandler.tryCatch for consistent error handling
20
+ return await ErrorHandler.tryCatch(async () => {
21
+ // Call the registration function
22
+ await registerFn(server, componentLogger);
23
+ componentLogger.info(`${options.type} registered successfully: ${options.name}`);
24
+ }, {
25
+ operation: `registering ${options.type}`,
26
+ // Provide context for better error tracking
27
+ context: {
28
+ componentType: options.type,
29
+ componentName: options.name
30
+ },
31
+ // Use a specific error code for registration failures
32
+ errorCode: BaseErrorCode.INTERNAL_ERROR,
33
+ // Custom error mapper for clearer error messages
34
+ errorMapper: (error) => new McpError(error instanceof McpError ? error.code : BaseErrorCode.INTERNAL_ERROR, `Failed to register ${options.type} '${options.name}': ${error instanceof Error ? error.message : 'Unknown error'}`, { componentType: options.type, componentName: options.name }),
35
+ // Registration errors are considered critical
36
+ critical: true
37
+ });
38
+ }
39
+ /**
40
+ * Register a tool with the MCP server using a consistent pattern
41
+ * @param server MCP server instance
42
+ * @param options Tool registration options
43
+ * @param handlerFn Function that sets up the tool handler
44
+ * @returns Promise resolving when registration is complete
45
+ */
46
+ export async function registerTool(server, options, handlerFn) {
47
+ return registerComponent(server, { ...options, type: 'tool' }, handlerFn);
48
+ }
49
+ /**
50
+ * Register a resource with the MCP server using a consistent pattern
51
+ * @param server MCP server instance
52
+ * @param options Resource registration options
53
+ * @param handlerFn Function that sets up the resource handler
54
+ * @returns Promise resolving when registration is complete
55
+ */
56
+ export async function registerResource(server, options, handlerFn) {
57
+ return registerComponent(server, { ...options, type: 'resource' }, handlerFn);
58
+ }
59
+ export default {
60
+ registerComponent,
61
+ registerTool,
62
+ registerResource
63
+ };
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Constants for the ntfy service
3
+ */
4
+ /** Default ntfy server URL */
5
+ export declare const DEFAULT_NTFY_BASE_URL = "https://ntfy.sh";
6
+ /** HTTP subscription format endpoints */
7
+ export declare const SUBSCRIPTION_ENDPOINTS: {
8
+ json: string;
9
+ sse: string;
10
+ raw: string;
11
+ ws: string;
12
+ };
13
+ /** Default subscription options */
14
+ export declare const DEFAULT_SUBSCRIPTION_OPTIONS: {
15
+ baseUrl: string;
16
+ poll: boolean;
17
+ scheduled: boolean;
18
+ };
19
+ /** Default HTTP request timeout in milliseconds */
20
+ export declare const DEFAULT_REQUEST_TIMEOUT = 30000;
21
+ /** Keepalive timeout in milliseconds (how long to wait before considering connection dead) */
22
+ export declare const KEEPALIVE_TIMEOUT = 120000;
23
+ /** Reconnect delay in milliseconds (delay before attempting to reconnect after failure) */
24
+ export declare const RECONNECT_DELAY = 5000;
25
+ /** Maximum reconnect attempts before giving up */
26
+ export declare const MAX_RECONNECT_ATTEMPTS = 5;
27
+ /** User agent string for requests */
28
+ export declare const USER_AGENT = "ntfy-mcp-server/1.0.0";
29
+ /** Error messages */
30
+ export declare const ERROR_MESSAGES: {
31
+ INVALID_TOPIC: string;
32
+ CONNECTION_FAILED: string;
33
+ SUBSCRIPTION_CLOSED: string;
34
+ PARSE_ERROR: string;
35
+ NETWORK_ERROR: string;
36
+ AUTHENTICATION_FAILED: string;
37
+ };
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Constants for the ntfy service
3
+ */
4
+ /** Default ntfy server URL */
5
+ export const DEFAULT_NTFY_BASE_URL = 'https://ntfy.sh';
6
+ /** HTTP subscription format endpoints */
7
+ export const SUBSCRIPTION_ENDPOINTS = {
8
+ json: '/json',
9
+ sse: '/sse',
10
+ raw: '/raw',
11
+ ws: '/ws',
12
+ };
13
+ /** Default subscription options */
14
+ export const DEFAULT_SUBSCRIPTION_OPTIONS = {
15
+ baseUrl: DEFAULT_NTFY_BASE_URL,
16
+ poll: false,
17
+ scheduled: false,
18
+ };
19
+ /** Default HTTP request timeout in milliseconds */
20
+ export const DEFAULT_REQUEST_TIMEOUT = 30000;
21
+ /** Keepalive timeout in milliseconds (how long to wait before considering connection dead) */
22
+ export const KEEPALIVE_TIMEOUT = 120000;
23
+ /** Reconnect delay in milliseconds (delay before attempting to reconnect after failure) */
24
+ export const RECONNECT_DELAY = 5000;
25
+ /** Maximum reconnect attempts before giving up */
26
+ export const MAX_RECONNECT_ATTEMPTS = 5;
27
+ /** User agent string for requests */
28
+ export const USER_AGENT = 'ntfy-mcp-server/1.0.0';
29
+ /** Error messages */
30
+ export const ERROR_MESSAGES = {
31
+ INVALID_TOPIC: 'Invalid topic name',
32
+ CONNECTION_FAILED: 'Failed to connect to ntfy server',
33
+ SUBSCRIPTION_CLOSED: 'Subscription closed',
34
+ PARSE_ERROR: 'Failed to parse message',
35
+ NETWORK_ERROR: 'Network error occurred',
36
+ AUTHENTICATION_FAILED: 'Authentication failed',
37
+ };
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Custom error classes for the ntfy service
3
+ */
4
+ import { McpError } from '../../types-global/errors.js';
5
+ /**
6
+ * Base error class for ntfy service errors
7
+ */
8
+ export declare class NtfyError extends McpError {
9
+ constructor(message: string, details?: Record<string, unknown>);
10
+ }
11
+ /**
12
+ * Error thrown when connection to ntfy server fails
13
+ */
14
+ export declare class NtfyConnectionError extends NtfyError {
15
+ readonly url?: string | undefined;
16
+ constructor(message: string, url?: string | undefined);
17
+ }
18
+ /**
19
+ * Error thrown when authentication fails
20
+ */
21
+ export declare class NtfyAuthenticationError extends NtfyError {
22
+ constructor(message: string);
23
+ }
24
+ /**
25
+ * Error thrown when a message cannot be parsed
26
+ */
27
+ export declare class NtfyParseError extends NtfyError {
28
+ readonly rawData?: string | undefined;
29
+ constructor(message: string, rawData?: string | undefined);
30
+ }
31
+ /**
32
+ * Error thrown when a subscription is closed unexpectedly
33
+ */
34
+ export declare class NtfySubscriptionClosedError extends NtfyError {
35
+ readonly reason?: string | undefined;
36
+ constructor(message: string, reason?: string | undefined);
37
+ }
38
+ /**
39
+ * Error thrown when an invalid topic name is provided
40
+ */
41
+ export declare class NtfyInvalidTopicError extends NtfyError {
42
+ readonly topic?: string | undefined;
43
+ constructor(message: string, topic?: string | undefined);
44
+ }
45
+ /**
46
+ * Error thrown when a timeout occurs
47
+ */
48
+ export declare class NtfyTimeoutError extends NtfyError {
49
+ readonly timeoutMs?: number | undefined;
50
+ constructor(message: string, timeoutMs?: number | undefined);
51
+ }
52
+ /**
53
+ * Error mapping for ntfy errors
54
+ */
55
+ export declare const NTFY_ERROR_MAPPINGS: ({
56
+ pattern: RegExp;
57
+ errorCode: "UNAUTHORIZED";
58
+ factory: (error: unknown) => NtfyAuthenticationError;
59
+ } | {
60
+ pattern: RegExp;
61
+ errorCode: "VALIDATION_ERROR";
62
+ factory: (error: unknown, context?: Record<string, unknown>) => NtfyParseError;
63
+ } | {
64
+ pattern: RegExp;
65
+ errorCode: "VALIDATION_ERROR";
66
+ factory: (error: unknown, context?: Record<string, unknown>) => NtfyInvalidTopicError;
67
+ } | {
68
+ pattern: RegExp;
69
+ errorCode: "TIMEOUT";
70
+ factory: (error: unknown, context?: Record<string, unknown>) => NtfyTimeoutError;
71
+ } | {
72
+ pattern: RegExp;
73
+ errorCode: "SERVICE_UNAVAILABLE";
74
+ factory: (error: unknown, context?: Record<string, unknown>) => NtfyConnectionError;
75
+ })[];
76
+ /**
77
+ * Create an error mapper function for ntfy errors
78
+ */
79
+ export declare const ntfyErrorMapper: (error: unknown, context?: Record<string, unknown>) => McpError;