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.
- package/LICENSE +201 -0
- package/README.md +423 -0
- package/dist/config/index.d.ts +23 -0
- package/dist/config/index.js +111 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +108 -0
- package/dist/mcp-server/resources/ntfyResource/getNtfyTopic.d.ts +2 -0
- package/dist/mcp-server/resources/ntfyResource/getNtfyTopic.js +111 -0
- package/dist/mcp-server/resources/ntfyResource/index.d.ts +12 -0
- package/dist/mcp-server/resources/ntfyResource/index.js +72 -0
- package/dist/mcp-server/resources/ntfyResource/types.d.ts +27 -0
- package/dist/mcp-server/resources/ntfyResource/types.js +8 -0
- package/dist/mcp-server/server.d.ts +40 -0
- package/dist/mcp-server/server.js +245 -0
- package/dist/mcp-server/tools/ntfyTool/index.d.ts +11 -0
- package/dist/mcp-server/tools/ntfyTool/index.js +110 -0
- package/dist/mcp-server/tools/ntfyTool/ntfyMessage.d.ts +9 -0
- package/dist/mcp-server/tools/ntfyTool/ntfyMessage.js +289 -0
- package/dist/mcp-server/tools/ntfyTool/types.d.ts +252 -0
- package/dist/mcp-server/tools/ntfyTool/types.js +144 -0
- package/dist/mcp-server/utils/registrationHelper.d.ts +48 -0
- package/dist/mcp-server/utils/registrationHelper.js +63 -0
- package/dist/services/ntfy/constants.d.ts +37 -0
- package/dist/services/ntfy/constants.js +37 -0
- package/dist/services/ntfy/errors.d.ts +79 -0
- package/dist/services/ntfy/errors.js +134 -0
- package/dist/services/ntfy/index.d.ts +33 -0
- package/dist/services/ntfy/index.js +56 -0
- package/dist/services/ntfy/publisher.d.ts +66 -0
- package/dist/services/ntfy/publisher.js +229 -0
- package/dist/services/ntfy/subscriber.d.ts +81 -0
- package/dist/services/ntfy/subscriber.js +502 -0
- package/dist/services/ntfy/types.d.ts +161 -0
- package/dist/services/ntfy/types.js +4 -0
- package/dist/services/ntfy/utils.d.ts +85 -0
- package/dist/services/ntfy/utils.js +410 -0
- package/dist/types-global/errors.d.ts +35 -0
- package/dist/types-global/errors.js +39 -0
- package/dist/types-global/mcp.d.ts +30 -0
- package/dist/types-global/mcp.js +25 -0
- package/dist/types-global/tool.d.ts +61 -0
- package/dist/types-global/tool.js +99 -0
- package/dist/utils/errorHandler.d.ts +98 -0
- package/dist/utils/errorHandler.js +271 -0
- package/dist/utils/idGenerator.d.ts +94 -0
- package/dist/utils/idGenerator.js +149 -0
- package/dist/utils/index.d.ts +13 -0
- package/dist/utils/index.js +16 -0
- package/dist/utils/logger.d.ts +36 -0
- package/dist/utils/logger.js +92 -0
- package/dist/utils/rateLimiter.d.ts +115 -0
- package/dist/utils/rateLimiter.js +180 -0
- package/dist/utils/requestContext.d.ts +68 -0
- package/dist/utils/requestContext.js +91 -0
- package/dist/utils/sanitization.d.ts +224 -0
- package/dist/utils/sanitization.js +367 -0
- package/dist/utils/security.d.ts +26 -0
- package/dist/utils/security.js +27 -0
- package/package.json +47 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { ErrorHandler } from "../utils/errorHandler.js";
|
|
2
|
+
import { logger } from "../utils/logger.js";
|
|
3
|
+
// Create a module-level logger
|
|
4
|
+
const toolLogger = logger.createChildLogger({
|
|
5
|
+
module: 'ToolRegistration'
|
|
6
|
+
});
|
|
7
|
+
/**
|
|
8
|
+
* Create a tool example
|
|
9
|
+
*
|
|
10
|
+
* @param input Example input parameters
|
|
11
|
+
* @param output Expected output (as a formatted string)
|
|
12
|
+
* @param description Description of what the example demonstrates
|
|
13
|
+
* @returns A tool example object
|
|
14
|
+
*/
|
|
15
|
+
export function createToolExample(input, output, description) {
|
|
16
|
+
return {
|
|
17
|
+
input,
|
|
18
|
+
output,
|
|
19
|
+
description
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Create tool metadata
|
|
24
|
+
*
|
|
25
|
+
* @param metadata Tool metadata options
|
|
26
|
+
* @returns Tool metadata configuration
|
|
27
|
+
*/
|
|
28
|
+
export function createToolMetadata(metadata) {
|
|
29
|
+
return metadata;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Register a tool with the MCP server
|
|
33
|
+
*
|
|
34
|
+
* This is a compatibility wrapper for the McpServer.tool() method.
|
|
35
|
+
* In the current implementation, the tool registration is handled by the McpServer class,
|
|
36
|
+
* so this function primarily exists to provide a consistent API.
|
|
37
|
+
*
|
|
38
|
+
* @param server MCP server instance
|
|
39
|
+
* @param name Tool name
|
|
40
|
+
* @param description Tool description
|
|
41
|
+
* @param inputSchema Schema for validating input
|
|
42
|
+
* @param handler Handler function for the tool
|
|
43
|
+
* @param metadata Optional tool metadata
|
|
44
|
+
*/
|
|
45
|
+
export function registerTool(server, // Using any to avoid type conflicts
|
|
46
|
+
name, description, inputSchema, handler, metadata) {
|
|
47
|
+
return ErrorHandler.tryCatch(async () => {
|
|
48
|
+
// Log the registration attempt
|
|
49
|
+
toolLogger.info(`Registering tool: ${name}`, {
|
|
50
|
+
toolName: name,
|
|
51
|
+
schemaKeys: Object.keys(inputSchema),
|
|
52
|
+
hasMetadata: Boolean(metadata),
|
|
53
|
+
hasExamples: Boolean(metadata?.examples?.length)
|
|
54
|
+
});
|
|
55
|
+
// Some basic validation
|
|
56
|
+
if (!name) {
|
|
57
|
+
throw new Error('Tool name is required');
|
|
58
|
+
}
|
|
59
|
+
if (!inputSchema) {
|
|
60
|
+
throw new Error('Input schema is required');
|
|
61
|
+
}
|
|
62
|
+
if (!handler || typeof handler !== 'function') {
|
|
63
|
+
throw new Error('Handler must be a function');
|
|
64
|
+
}
|
|
65
|
+
// Convert schema to a more standardized format if needed
|
|
66
|
+
const schemaDescription = Object.entries(inputSchema).map(([key, schema]) => {
|
|
67
|
+
const description = schema.description;
|
|
68
|
+
const isRequired = !schema.isOptional?.();
|
|
69
|
+
return `${key}${isRequired ? ' (required)' : ''}: ${description || 'No description'}`;
|
|
70
|
+
}).join('\n');
|
|
71
|
+
toolLogger.debug(`Tool ${name} schema:`, {
|
|
72
|
+
toolName: name,
|
|
73
|
+
schema: schemaDescription
|
|
74
|
+
});
|
|
75
|
+
// Actually register the tool with the server
|
|
76
|
+
// Check if it's an McpServer instance with tool() method
|
|
77
|
+
if (server.tool && typeof server.tool === 'function') {
|
|
78
|
+
// Use the McpServer.tool() method directly
|
|
79
|
+
toolLogger.debug('Using McpServer.tool() method');
|
|
80
|
+
server.tool(name, inputSchema, handler, {
|
|
81
|
+
description,
|
|
82
|
+
examples: metadata?.examples
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
// For other server types or for testing, log a warning
|
|
87
|
+
toolLogger.warn(`Unable to register tool ${name} with server - missing tool() method`, {
|
|
88
|
+
toolName: name
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
// Log successful registration
|
|
92
|
+
toolLogger.info(`Tool ${name} registered successfully`);
|
|
93
|
+
}, {
|
|
94
|
+
context: { toolName: name },
|
|
95
|
+
operation: "registering tool",
|
|
96
|
+
errorMapper: (error) => new Error(`Failed to register tool ${name}: ${error instanceof Error ? error.message : String(error)}`),
|
|
97
|
+
rethrow: true
|
|
98
|
+
});
|
|
99
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { BaseErrorCode, McpError } from '../types-global/errors.js';
|
|
2
|
+
/**
|
|
3
|
+
* Generic error context interface
|
|
4
|
+
*/
|
|
5
|
+
export interface ErrorContext {
|
|
6
|
+
/** Unique request or operation identifier */
|
|
7
|
+
requestId?: string;
|
|
8
|
+
/** Any additional context information */
|
|
9
|
+
[key: string]: unknown;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Error handler options
|
|
13
|
+
*/
|
|
14
|
+
export interface ErrorHandlerOptions {
|
|
15
|
+
/** The context of the operation that caused the error */
|
|
16
|
+
context?: ErrorContext;
|
|
17
|
+
/** The name of the operation being performed */
|
|
18
|
+
operation: string;
|
|
19
|
+
/** The input that caused the error */
|
|
20
|
+
input?: unknown;
|
|
21
|
+
/** Whether to rethrow the error after handling */
|
|
22
|
+
rethrow?: boolean;
|
|
23
|
+
/** Custom error code to use when creating an McpError */
|
|
24
|
+
errorCode?: BaseErrorCode;
|
|
25
|
+
/** Custom error mapper function */
|
|
26
|
+
errorMapper?: (error: unknown) => Error;
|
|
27
|
+
/** Whether to include stack traces in logs */
|
|
28
|
+
includeStack?: boolean;
|
|
29
|
+
/** Whether this is a critical error that should abort operations */
|
|
30
|
+
critical?: boolean;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Base error mapping rule
|
|
34
|
+
*/
|
|
35
|
+
export interface BaseErrorMapping {
|
|
36
|
+
/** Pattern to match in the error message */
|
|
37
|
+
pattern: string | RegExp;
|
|
38
|
+
/** Error code for mapped errors */
|
|
39
|
+
errorCode: BaseErrorCode;
|
|
40
|
+
/** Custom error message template */
|
|
41
|
+
messageTemplate?: string;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Error mapping configuration
|
|
45
|
+
*/
|
|
46
|
+
export interface ErrorMapping<T extends Error = Error> extends BaseErrorMapping {
|
|
47
|
+
/** Factory function to create the mapped error */
|
|
48
|
+
factory: (error: unknown, context?: Record<string, unknown>) => T;
|
|
49
|
+
/** Additional context to merge with error context */
|
|
50
|
+
additionalContext?: Record<string, unknown>;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Error handler utility class with various error handling methods
|
|
54
|
+
*/
|
|
55
|
+
export declare class ErrorHandler {
|
|
56
|
+
/**
|
|
57
|
+
* Determine the appropriate error code for an error based on patterns and type
|
|
58
|
+
* @param error The error to classify
|
|
59
|
+
* @returns The appropriate error code
|
|
60
|
+
*/
|
|
61
|
+
static determineErrorCode(error: unknown): BaseErrorCode;
|
|
62
|
+
/**
|
|
63
|
+
* Handle operation errors with consistent logging and transformation
|
|
64
|
+
* @param error The error that occurred
|
|
65
|
+
* @param options Error handling options
|
|
66
|
+
* @returns The transformed error
|
|
67
|
+
*/
|
|
68
|
+
static handleError(error: unknown, options: ErrorHandlerOptions): Error;
|
|
69
|
+
/**
|
|
70
|
+
* Map an error to a specific error type based on error message patterns
|
|
71
|
+
* @param error The error to map
|
|
72
|
+
* @param mappings Array of pattern and factory mappings
|
|
73
|
+
* @param defaultFactory Default factory function if no pattern matches
|
|
74
|
+
* @returns The mapped error
|
|
75
|
+
*/
|
|
76
|
+
static mapError<T extends Error>(error: unknown, mappings: ErrorMapping<T>[], defaultFactory?: (error: unknown, context?: Record<string, unknown>) => T): T | Error;
|
|
77
|
+
/**
|
|
78
|
+
* Create a simplified error mapper based on error patterns and codes
|
|
79
|
+
* @param patterns Array of error patterns, codes, and messages
|
|
80
|
+
* @param defaultErrorCode Default error code if no pattern matches
|
|
81
|
+
* @returns Error mapper function
|
|
82
|
+
*/
|
|
83
|
+
static createErrorMapper(patterns: BaseErrorMapping[], defaultErrorCode?: BaseErrorCode): (error: unknown, context?: Record<string, unknown>) => McpError;
|
|
84
|
+
/**
|
|
85
|
+
* Format an error for consistent response structure
|
|
86
|
+
* @param error The error to format
|
|
87
|
+
* @returns Formatted error object
|
|
88
|
+
*/
|
|
89
|
+
static formatError(error: unknown): Record<string, unknown>;
|
|
90
|
+
/**
|
|
91
|
+
* Safely execute a function and handle any errors
|
|
92
|
+
* @param fn Function to execute
|
|
93
|
+
* @param options Error handling options
|
|
94
|
+
* @returns The result of the function or error
|
|
95
|
+
*/
|
|
96
|
+
static tryCatch<T>(fn: () => Promise<T> | T, options: ErrorHandlerOptions): Promise<T>;
|
|
97
|
+
}
|
|
98
|
+
export default ErrorHandler;
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import { BaseErrorCode, McpError } from '../types-global/errors.js';
|
|
2
|
+
import { logger } from './logger.js';
|
|
3
|
+
import { sanitizeInputForLogging } from './security.js';
|
|
4
|
+
/**
|
|
5
|
+
* Simple mapper that maps error types to error codes
|
|
6
|
+
*/
|
|
7
|
+
const ERROR_TYPE_MAPPINGS = {
|
|
8
|
+
'SyntaxError': BaseErrorCode.VALIDATION_ERROR,
|
|
9
|
+
'TypeError': BaseErrorCode.VALIDATION_ERROR,
|
|
10
|
+
'ReferenceError': BaseErrorCode.INTERNAL_ERROR,
|
|
11
|
+
'RangeError': BaseErrorCode.VALIDATION_ERROR,
|
|
12
|
+
'URIError': BaseErrorCode.VALIDATION_ERROR,
|
|
13
|
+
'EvalError': BaseErrorCode.INTERNAL_ERROR
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Common error patterns for automatic classification
|
|
17
|
+
*/
|
|
18
|
+
const COMMON_ERROR_PATTERNS = [
|
|
19
|
+
// Authentication related errors
|
|
20
|
+
{ pattern: /auth|unauthorized|unauthenticated|not.*logged.*in|invalid.*token|expired.*token/i, errorCode: BaseErrorCode.UNAUTHORIZED },
|
|
21
|
+
// Permission related errors
|
|
22
|
+
{ pattern: /permission|forbidden|access.*denied|not.*allowed/i, errorCode: BaseErrorCode.FORBIDDEN },
|
|
23
|
+
// Not found errors
|
|
24
|
+
{ pattern: /not.*found|missing|no.*such|doesn't.*exist|couldn't.*find/i, errorCode: BaseErrorCode.NOT_FOUND },
|
|
25
|
+
// Validation errors
|
|
26
|
+
{ pattern: /invalid|validation|malformed|bad request|wrong format/i, errorCode: BaseErrorCode.VALIDATION_ERROR },
|
|
27
|
+
// Conflict errors
|
|
28
|
+
{ pattern: /conflict|already.*exists|duplicate|unique.*constraint/i, errorCode: BaseErrorCode.CONFLICT },
|
|
29
|
+
// Rate limiting
|
|
30
|
+
{ pattern: /rate.*limit|too.*many.*requests|throttled/i, errorCode: BaseErrorCode.RATE_LIMITED },
|
|
31
|
+
// Timeout errors
|
|
32
|
+
{ pattern: /timeout|timed.*out|deadline.*exceeded/i, errorCode: BaseErrorCode.TIMEOUT },
|
|
33
|
+
// External service errors
|
|
34
|
+
{ pattern: /service.*unavailable|bad.*gateway|gateway.*timeout/i, errorCode: BaseErrorCode.SERVICE_UNAVAILABLE }
|
|
35
|
+
];
|
|
36
|
+
/**
|
|
37
|
+
* Get a readable name for an error
|
|
38
|
+
* @param error Error to get name for
|
|
39
|
+
* @returns User-friendly error name
|
|
40
|
+
*/
|
|
41
|
+
function getErrorName(error) {
|
|
42
|
+
if (error instanceof Error) {
|
|
43
|
+
return error.name || 'Error';
|
|
44
|
+
}
|
|
45
|
+
if (error === null) {
|
|
46
|
+
return 'NullError';
|
|
47
|
+
}
|
|
48
|
+
if (error === undefined) {
|
|
49
|
+
return 'UndefinedError';
|
|
50
|
+
}
|
|
51
|
+
return typeof error === 'object'
|
|
52
|
+
? 'ObjectError'
|
|
53
|
+
: 'UnknownError';
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Get a message from an error
|
|
57
|
+
* @param error Error to get message from
|
|
58
|
+
* @returns Error message
|
|
59
|
+
*/
|
|
60
|
+
function getErrorMessage(error) {
|
|
61
|
+
if (error instanceof Error) {
|
|
62
|
+
return error.message;
|
|
63
|
+
}
|
|
64
|
+
if (error === null) {
|
|
65
|
+
return 'Null error occurred';
|
|
66
|
+
}
|
|
67
|
+
if (error === undefined) {
|
|
68
|
+
return 'Undefined error occurred';
|
|
69
|
+
}
|
|
70
|
+
return typeof error === 'string'
|
|
71
|
+
? error
|
|
72
|
+
: String(error);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Error handler utility class with various error handling methods
|
|
76
|
+
*/
|
|
77
|
+
export class ErrorHandler {
|
|
78
|
+
/**
|
|
79
|
+
* Determine the appropriate error code for an error based on patterns and type
|
|
80
|
+
* @param error The error to classify
|
|
81
|
+
* @returns The appropriate error code
|
|
82
|
+
*/
|
|
83
|
+
static determineErrorCode(error) {
|
|
84
|
+
// If it's already an McpError, use its code
|
|
85
|
+
if (error instanceof McpError) {
|
|
86
|
+
return error.code;
|
|
87
|
+
}
|
|
88
|
+
const errorName = getErrorName(error);
|
|
89
|
+
const errorMessage = getErrorMessage(error);
|
|
90
|
+
// Check if the error type has a direct mapping
|
|
91
|
+
if (errorName in ERROR_TYPE_MAPPINGS) {
|
|
92
|
+
return ERROR_TYPE_MAPPINGS[errorName];
|
|
93
|
+
}
|
|
94
|
+
// Check for common error patterns
|
|
95
|
+
for (const pattern of COMMON_ERROR_PATTERNS) {
|
|
96
|
+
const regex = pattern.pattern instanceof RegExp
|
|
97
|
+
? pattern.pattern
|
|
98
|
+
: new RegExp(pattern.pattern, 'i');
|
|
99
|
+
if (regex.test(errorMessage) || regex.test(errorName)) {
|
|
100
|
+
return pattern.errorCode;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// Default to internal error if no pattern matches
|
|
104
|
+
return BaseErrorCode.INTERNAL_ERROR;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Handle operation errors with consistent logging and transformation
|
|
108
|
+
* @param error The error that occurred
|
|
109
|
+
* @param options Error handling options
|
|
110
|
+
* @returns The transformed error
|
|
111
|
+
*/
|
|
112
|
+
static handleError(error, options) {
|
|
113
|
+
const { context, operation, input, rethrow = false, errorCode: explicitErrorCode, includeStack = true, critical = false } = options;
|
|
114
|
+
// If it's already an McpError, use it directly but apply additional context
|
|
115
|
+
if (error instanceof McpError) {
|
|
116
|
+
// Add any additional context
|
|
117
|
+
if (context && Object.keys(context).length > 0) {
|
|
118
|
+
error.details = { ...error.details, ...context };
|
|
119
|
+
}
|
|
120
|
+
// Log the error with sanitized input
|
|
121
|
+
logger.error(`Error ${operation}: ${error.message}`, {
|
|
122
|
+
errorCode: error.code,
|
|
123
|
+
requestId: context?.requestId,
|
|
124
|
+
input: input ? sanitizeInputForLogging(input) : undefined,
|
|
125
|
+
stack: includeStack ? error.stack : undefined,
|
|
126
|
+
critical,
|
|
127
|
+
...context
|
|
128
|
+
});
|
|
129
|
+
if (rethrow) {
|
|
130
|
+
throw error;
|
|
131
|
+
}
|
|
132
|
+
return error;
|
|
133
|
+
}
|
|
134
|
+
// Sanitize input for logging
|
|
135
|
+
const sanitizedInput = input ? sanitizeInputForLogging(input) : undefined;
|
|
136
|
+
// Log the error with consistent format
|
|
137
|
+
logger.error(`Error ${operation}`, {
|
|
138
|
+
error: error instanceof Error ? error.message : String(error),
|
|
139
|
+
errorType: getErrorName(error),
|
|
140
|
+
input: sanitizedInput,
|
|
141
|
+
requestId: context?.requestId,
|
|
142
|
+
stack: includeStack && error instanceof Error ? error.stack : undefined,
|
|
143
|
+
critical,
|
|
144
|
+
...context
|
|
145
|
+
});
|
|
146
|
+
// Choose the error code (explicit > determined > default)
|
|
147
|
+
const errorCode = explicitErrorCode ||
|
|
148
|
+
ErrorHandler.determineErrorCode(error) ||
|
|
149
|
+
BaseErrorCode.INTERNAL_ERROR;
|
|
150
|
+
// Transform to appropriate error type
|
|
151
|
+
const transformedError = options.errorMapper
|
|
152
|
+
? options.errorMapper(error)
|
|
153
|
+
: new McpError(errorCode, `Error ${operation}: ${error instanceof Error ? error.message : 'Unknown error'}`, {
|
|
154
|
+
originalError: getErrorName(error),
|
|
155
|
+
...context
|
|
156
|
+
});
|
|
157
|
+
// Rethrow if requested
|
|
158
|
+
if (rethrow) {
|
|
159
|
+
throw transformedError;
|
|
160
|
+
}
|
|
161
|
+
return transformedError;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Map an error to a specific error type based on error message patterns
|
|
165
|
+
* @param error The error to map
|
|
166
|
+
* @param mappings Array of pattern and factory mappings
|
|
167
|
+
* @param defaultFactory Default factory function if no pattern matches
|
|
168
|
+
* @returns The mapped error
|
|
169
|
+
*/
|
|
170
|
+
static mapError(error, mappings, defaultFactory) {
|
|
171
|
+
// If it's already the target type and we have a default factory to check against, return it
|
|
172
|
+
if (defaultFactory && error instanceof Error) {
|
|
173
|
+
const defaultInstance = defaultFactory(error);
|
|
174
|
+
if (error.constructor === defaultInstance.constructor) {
|
|
175
|
+
return error;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
const errorMessage = getErrorMessage(error);
|
|
179
|
+
// Check each pattern and return the first match
|
|
180
|
+
for (const mapping of mappings) {
|
|
181
|
+
const matches = mapping.pattern instanceof RegExp
|
|
182
|
+
? mapping.pattern.test(errorMessage)
|
|
183
|
+
: errorMessage.includes(mapping.pattern);
|
|
184
|
+
if (matches) {
|
|
185
|
+
return mapping.factory(error, mapping.additionalContext);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
// Return default or original error
|
|
189
|
+
if (defaultFactory) {
|
|
190
|
+
return defaultFactory(error);
|
|
191
|
+
}
|
|
192
|
+
return error instanceof Error
|
|
193
|
+
? error
|
|
194
|
+
: new Error(String(error));
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Create a simplified error mapper based on error patterns and codes
|
|
198
|
+
* @param patterns Array of error patterns, codes, and messages
|
|
199
|
+
* @param defaultErrorCode Default error code if no pattern matches
|
|
200
|
+
* @returns Error mapper function
|
|
201
|
+
*/
|
|
202
|
+
static createErrorMapper(patterns, defaultErrorCode = BaseErrorCode.INTERNAL_ERROR) {
|
|
203
|
+
return (error, context) => {
|
|
204
|
+
// Already an McpError
|
|
205
|
+
if (error instanceof McpError) {
|
|
206
|
+
// Add any additional context
|
|
207
|
+
if (context && Object.keys(context).length > 0) {
|
|
208
|
+
error.details = { ...error.details, ...context };
|
|
209
|
+
}
|
|
210
|
+
return error;
|
|
211
|
+
}
|
|
212
|
+
const errorMessage = getErrorMessage(error);
|
|
213
|
+
// Check each pattern for a match
|
|
214
|
+
for (const { pattern, errorCode, messageTemplate } of patterns) {
|
|
215
|
+
const matches = pattern instanceof RegExp
|
|
216
|
+
? pattern.test(errorMessage)
|
|
217
|
+
: errorMessage.includes(pattern);
|
|
218
|
+
if (matches) {
|
|
219
|
+
// Use template if provided, otherwise use original error message
|
|
220
|
+
const message = messageTemplate
|
|
221
|
+
? messageTemplate.replace('{message}', errorMessage)
|
|
222
|
+
: errorMessage;
|
|
223
|
+
return new McpError(errorCode, message, { originalError: getErrorName(error), ...context });
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
// No matches found, use default
|
|
227
|
+
return new McpError(defaultErrorCode, errorMessage, { originalError: getErrorName(error), ...context });
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Format an error for consistent response structure
|
|
232
|
+
* @param error The error to format
|
|
233
|
+
* @returns Formatted error object
|
|
234
|
+
*/
|
|
235
|
+
static formatError(error) {
|
|
236
|
+
if (error instanceof McpError) {
|
|
237
|
+
return {
|
|
238
|
+
code: error.code,
|
|
239
|
+
message: error.message,
|
|
240
|
+
details: error.details || {}
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
if (error instanceof Error) {
|
|
244
|
+
return {
|
|
245
|
+
code: ErrorHandler.determineErrorCode(error),
|
|
246
|
+
message: error.message,
|
|
247
|
+
details: { errorType: error.name }
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
return {
|
|
251
|
+
code: BaseErrorCode.UNKNOWN_ERROR,
|
|
252
|
+
message: String(error),
|
|
253
|
+
details: { errorType: typeof error }
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Safely execute a function and handle any errors
|
|
258
|
+
* @param fn Function to execute
|
|
259
|
+
* @param options Error handling options
|
|
260
|
+
* @returns The result of the function or error
|
|
261
|
+
*/
|
|
262
|
+
static async tryCatch(fn, options) {
|
|
263
|
+
try {
|
|
264
|
+
return await fn();
|
|
265
|
+
}
|
|
266
|
+
catch (error) {
|
|
267
|
+
throw ErrorHandler.handleError(error, { ...options, rethrow: true });
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
export default ErrorHandler;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interface for entity prefix configuration
|
|
3
|
+
*/
|
|
4
|
+
export interface EntityPrefixConfig {
|
|
5
|
+
[key: string]: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* ID Generation Options
|
|
9
|
+
*/
|
|
10
|
+
export interface IdGenerationOptions {
|
|
11
|
+
length?: number;
|
|
12
|
+
separator?: string;
|
|
13
|
+
charset?: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Generic ID Generator class for creating and managing unique identifiers
|
|
17
|
+
*/
|
|
18
|
+
export declare class IdGenerator {
|
|
19
|
+
private static DEFAULT_CHARSET;
|
|
20
|
+
private static DEFAULT_SEPARATOR;
|
|
21
|
+
private static DEFAULT_LENGTH;
|
|
22
|
+
private entityPrefixes;
|
|
23
|
+
private prefixToEntityType;
|
|
24
|
+
/**
|
|
25
|
+
* Constructor that accepts entity prefix configuration
|
|
26
|
+
* @param entityPrefixes Map of entity types to their prefixes
|
|
27
|
+
*/
|
|
28
|
+
constructor(entityPrefixes?: EntityPrefixConfig);
|
|
29
|
+
/**
|
|
30
|
+
* Set or update entity prefixes and rebuild the reverse lookup
|
|
31
|
+
* @param entityPrefixes Map of entity types to their prefixes
|
|
32
|
+
*/
|
|
33
|
+
setEntityPrefixes(entityPrefixes: EntityPrefixConfig): void;
|
|
34
|
+
/**
|
|
35
|
+
* Get all registered entity prefixes
|
|
36
|
+
* @returns The entity prefix configuration
|
|
37
|
+
*/
|
|
38
|
+
getEntityPrefixes(): EntityPrefixConfig;
|
|
39
|
+
/**
|
|
40
|
+
* Generates a cryptographically secure random alphanumeric string
|
|
41
|
+
* @param length The length of the random string to generate
|
|
42
|
+
* @param charset Optional custom character set
|
|
43
|
+
* @returns Random alphanumeric string
|
|
44
|
+
*/
|
|
45
|
+
generateRandomString(length?: number, charset?: string): string;
|
|
46
|
+
/**
|
|
47
|
+
* Generates a unique ID with an optional prefix
|
|
48
|
+
* @param prefix Optional prefix to add to the ID
|
|
49
|
+
* @param options Optional generation options
|
|
50
|
+
* @returns A unique identifier string
|
|
51
|
+
*/
|
|
52
|
+
generate(prefix?: string, options?: IdGenerationOptions): string;
|
|
53
|
+
/**
|
|
54
|
+
* Generates a custom ID for an entity with format PREFIX_XXXXXX
|
|
55
|
+
* @param entityType The type of entity to generate an ID for
|
|
56
|
+
* @param options Optional generation options
|
|
57
|
+
* @returns A unique identifier string (e.g., "PROJ_A6B3J0")
|
|
58
|
+
* @throws {McpError} If the entity type is not registered
|
|
59
|
+
*/
|
|
60
|
+
generateForEntity(entityType: string, options?: IdGenerationOptions): string;
|
|
61
|
+
/**
|
|
62
|
+
* Validates if a given ID matches the expected format for an entity type
|
|
63
|
+
* @param id The ID to validate
|
|
64
|
+
* @param entityType The expected entity type
|
|
65
|
+
* @param options Optional validation options
|
|
66
|
+
* @returns boolean indicating if the ID is valid
|
|
67
|
+
*/
|
|
68
|
+
isValid(id: string, entityType: string, options?: IdGenerationOptions): boolean;
|
|
69
|
+
/**
|
|
70
|
+
* Strips the prefix from an ID
|
|
71
|
+
* @param id The ID to strip
|
|
72
|
+
* @param separator Optional custom separator
|
|
73
|
+
* @returns The ID without the prefix
|
|
74
|
+
*/
|
|
75
|
+
stripPrefix(id: string, separator?: string): string;
|
|
76
|
+
/**
|
|
77
|
+
* Determines the entity type from an ID
|
|
78
|
+
* @param id The ID to get the entity type for
|
|
79
|
+
* @param separator Optional custom separator
|
|
80
|
+
* @returns The entity type
|
|
81
|
+
* @throws {McpError} If the ID format is invalid or entity type is unknown
|
|
82
|
+
*/
|
|
83
|
+
getEntityType(id: string, separator?: string): string;
|
|
84
|
+
/**
|
|
85
|
+
* Normalizes an entity ID to ensure consistent uppercase format
|
|
86
|
+
* @param id The ID to normalize
|
|
87
|
+
* @param separator Optional custom separator
|
|
88
|
+
* @returns The normalized ID in uppercase format
|
|
89
|
+
*/
|
|
90
|
+
normalize(id: string, separator?: string): string;
|
|
91
|
+
}
|
|
92
|
+
export declare const idGenerator: IdGenerator;
|
|
93
|
+
export declare const generateUUID: () => string;
|
|
94
|
+
export default idGenerator;
|