mcp-ts-template 1.1.6
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 +233 -0
- package/dist/config/index.d.ts +73 -0
- package/dist/config/index.js +125 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +162 -0
- package/dist/mcp-client/client.d.ts +36 -0
- package/dist/mcp-client/client.js +276 -0
- package/dist/mcp-client/configLoader.d.ts +75 -0
- package/dist/mcp-client/configLoader.js +203 -0
- package/dist/mcp-client/index.d.ts +10 -0
- package/dist/mcp-client/index.js +14 -0
- package/dist/mcp-client/transport.d.ts +34 -0
- package/dist/mcp-client/transport.js +183 -0
- package/dist/mcp-server/resources/echoResource/echoResourceLogic.d.ts +38 -0
- package/dist/mcp-server/resources/echoResource/echoResourceLogic.js +40 -0
- package/dist/mcp-server/resources/echoResource/index.d.ts +5 -0
- package/dist/mcp-server/resources/echoResource/index.js +5 -0
- package/dist/mcp-server/resources/echoResource/registration.d.ts +12 -0
- package/dist/mcp-server/resources/echoResource/registration.js +122 -0
- package/dist/mcp-server/server.d.ts +27 -0
- package/dist/mcp-server/server.js +176 -0
- package/dist/mcp-server/tools/echoTool/echoToolLogic.d.ts +68 -0
- package/dist/mcp-server/tools/echoTool/echoToolLogic.js +73 -0
- package/dist/mcp-server/tools/echoTool/index.d.ts +5 -0
- package/dist/mcp-server/tools/echoTool/index.js +5 -0
- package/dist/mcp-server/tools/echoTool/registration.d.ts +12 -0
- package/dist/mcp-server/tools/echoTool/registration.js +86 -0
- package/dist/mcp-server/transports/authentication/authMiddleware.d.ts +57 -0
- package/dist/mcp-server/transports/authentication/authMiddleware.js +145 -0
- package/dist/mcp-server/transports/httpTransport.d.ts +23 -0
- package/dist/mcp-server/transports/httpTransport.js +411 -0
- package/dist/mcp-server/transports/stdioTransport.d.ts +40 -0
- package/dist/mcp-server/transports/stdioTransport.js +70 -0
- package/dist/types-global/errors.d.ts +73 -0
- package/dist/types-global/errors.js +66 -0
- package/dist/utils/index.d.ts +4 -0
- package/dist/utils/index.js +12 -0
- package/dist/utils/internal/errorHandler.d.ts +90 -0
- package/dist/utils/internal/errorHandler.js +247 -0
- package/dist/utils/internal/index.d.ts +3 -0
- package/dist/utils/internal/index.js +3 -0
- package/dist/utils/internal/logger.d.ts +50 -0
- package/dist/utils/internal/logger.js +267 -0
- package/dist/utils/internal/requestContext.d.ts +47 -0
- package/dist/utils/internal/requestContext.js +48 -0
- package/dist/utils/metrics/index.d.ts +1 -0
- package/dist/utils/metrics/index.js +1 -0
- package/dist/utils/metrics/tokenCounter.d.ts +27 -0
- package/dist/utils/metrics/tokenCounter.js +124 -0
- package/dist/utils/parsing/dateParser.d.ts +27 -0
- package/dist/utils/parsing/dateParser.js +62 -0
- package/dist/utils/parsing/index.d.ts +2 -0
- package/dist/utils/parsing/index.js +2 -0
- package/dist/utils/parsing/jsonParser.d.ts +46 -0
- package/dist/utils/parsing/jsonParser.js +79 -0
- package/dist/utils/security/idGenerator.d.ts +93 -0
- package/dist/utils/security/idGenerator.js +147 -0
- package/dist/utils/security/index.d.ts +3 -0
- package/dist/utils/security/index.js +3 -0
- package/dist/utils/security/rateLimiter.d.ts +92 -0
- package/dist/utils/security/rateLimiter.js +171 -0
- package/dist/utils/security/sanitization.d.ts +180 -0
- package/dist/utils/security/sanitization.js +372 -0
- package/package.json +79 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Handles the setup and connection for the Stdio MCP transport.
|
|
3
|
+
* Implements the MCP Specification 2025-03-26 for stdio transport.
|
|
4
|
+
* This transport communicates directly over standard input (stdin) and
|
|
5
|
+
* standard output (stdout), typically used when the MCP server is launched
|
|
6
|
+
* as a child process by a host application.
|
|
7
|
+
*
|
|
8
|
+
* Specification Reference:
|
|
9
|
+
* https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-03-26/basic/transports.mdx#stdio
|
|
10
|
+
*
|
|
11
|
+
* --- Authentication Note ---
|
|
12
|
+
* As per the MCP Authorization Specification (2025-03-26, Section 1.2),
|
|
13
|
+
* STDIO transports SHOULD NOT implement HTTP-based authentication flows.
|
|
14
|
+
* Authorization is typically handled implicitly by the host application
|
|
15
|
+
* controlling the server process. This implementation follows that guideline.
|
|
16
|
+
*
|
|
17
|
+
* @see {@link https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-03-26/basic/authorization.mdx | MCP Authorization Specification}
|
|
18
|
+
*/
|
|
19
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
20
|
+
// Import core utilities: ErrorHandler for centralized error management and logger for logging.
|
|
21
|
+
import { ErrorHandler, logger } from '../../utils/index.js';
|
|
22
|
+
/**
|
|
23
|
+
* Connects a given McpServer instance to the Stdio transport. (Asynchronous)
|
|
24
|
+
* Initializes the SDK's StdioServerTransport, which handles reading newline-delimited
|
|
25
|
+
* JSON-RPC messages from process.stdin and writing corresponding messages to process.stdout,
|
|
26
|
+
* adhering to the MCP stdio transport specification.
|
|
27
|
+
*
|
|
28
|
+
* MCP Spec Points Covered by SDK's StdioServerTransport:
|
|
29
|
+
* - Reads JSON-RPC messages (requests, notifications, responses, batches) from stdin.
|
|
30
|
+
* - Writes JSON-RPC messages to stdout.
|
|
31
|
+
* - Handles newline delimiters and ensures no embedded newlines in output messages.
|
|
32
|
+
* - Ensures only valid MCP messages are written to stdout.
|
|
33
|
+
*
|
|
34
|
+
* Note: Logging via the `logger` utility MAY result in output to stderr, which is
|
|
35
|
+
* permitted by the spec for logging purposes.
|
|
36
|
+
*
|
|
37
|
+
* @param {McpServer} server - The McpServer instance containing the core logic (tools, resources).
|
|
38
|
+
* @param {Record<string, any>} context - Logging context for correlation.
|
|
39
|
+
* @returns {Promise<void>} A promise that resolves when the connection is successfully established.
|
|
40
|
+
* @throws {Error} Throws an error if the connection fails during setup (e.g., issues connecting server to transport).
|
|
41
|
+
*/
|
|
42
|
+
export async function connectStdioTransport(server, context) {
|
|
43
|
+
// Add a specific operation name to the context for better log filtering.
|
|
44
|
+
const operationContext = { ...context, operation: 'connectStdioTransport', transportType: 'Stdio' };
|
|
45
|
+
logger.debug('Attempting to connect stdio transport...', operationContext);
|
|
46
|
+
try {
|
|
47
|
+
logger.debug('Creating StdioServerTransport instance...', operationContext);
|
|
48
|
+
// Instantiate the transport provided by the SDK for standard I/O communication.
|
|
49
|
+
// This class encapsulates the logic for reading from stdin and writing to stdout
|
|
50
|
+
// according to the MCP stdio spec.
|
|
51
|
+
const transport = new StdioServerTransport();
|
|
52
|
+
logger.debug('Connecting McpServer instance to StdioServerTransport...', operationContext);
|
|
53
|
+
// Establish the link between the server's core logic and the transport layer.
|
|
54
|
+
// This internally starts the necessary listeners on process.stdin.
|
|
55
|
+
await server.connect(transport);
|
|
56
|
+
// Log successful connection. The server is now ready to process messages via stdio.
|
|
57
|
+
logger.info('MCP Server connected and listening via stdio transport.', operationContext);
|
|
58
|
+
// Use console.log for prominent startup message visibility when run directly, only if TTY.
|
|
59
|
+
if (process.stdout.isTTY) {
|
|
60
|
+
console.log(`\n🚀 MCP Server running in STDIO mode.\n (MCP Spec: 2025-03-26 Stdio Transport)\n`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
// Catch and handle any critical errors during the transport connection setup.
|
|
65
|
+
// Mark as critical because the server cannot function without a connected transport.
|
|
66
|
+
ErrorHandler.handleError(err, { ...operationContext, critical: true });
|
|
67
|
+
// Rethrow the error to signal the failure to the calling code (e.g., the main server startup).
|
|
68
|
+
throw err;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Defines a set of standardized error codes for common issues within MCP servers or tools.
|
|
4
|
+
* These codes help clients understand the nature of an error programmatically.
|
|
5
|
+
*/
|
|
6
|
+
export declare enum BaseErrorCode {
|
|
7
|
+
/** Access denied due to invalid credentials or lack of authentication. */
|
|
8
|
+
UNAUTHORIZED = "UNAUTHORIZED",
|
|
9
|
+
/** Access denied despite valid authentication, due to insufficient permissions. */
|
|
10
|
+
FORBIDDEN = "FORBIDDEN",
|
|
11
|
+
/** The requested resource or entity could not be found. */
|
|
12
|
+
NOT_FOUND = "NOT_FOUND",
|
|
13
|
+
/** The request could not be completed due to a conflict with the current state of the resource. */
|
|
14
|
+
CONFLICT = "CONFLICT",
|
|
15
|
+
/** The request failed due to invalid input parameters or data. */
|
|
16
|
+
VALIDATION_ERROR = "VALIDATION_ERROR",
|
|
17
|
+
/** An error occurred while parsing input data (e.g., date string, JSON). */
|
|
18
|
+
PARSING_ERROR = "PARSING_ERROR",
|
|
19
|
+
/** The request was rejected because the client has exceeded rate limits. */
|
|
20
|
+
RATE_LIMITED = "RATE_LIMITED",
|
|
21
|
+
/** The request timed out before a response could be generated. */
|
|
22
|
+
TIMEOUT = "TIMEOUT",
|
|
23
|
+
/** The service is temporarily unavailable, possibly due to maintenance or overload. */
|
|
24
|
+
SERVICE_UNAVAILABLE = "SERVICE_UNAVAILABLE",
|
|
25
|
+
/** An unexpected error occurred on the server side. */
|
|
26
|
+
INTERNAL_ERROR = "INTERNAL_ERROR",
|
|
27
|
+
/** An error occurred, but the specific cause is unknown or cannot be categorized. */
|
|
28
|
+
UNKNOWN_ERROR = "UNKNOWN_ERROR",
|
|
29
|
+
/** An error occurred during the loading or validation of configuration data. */
|
|
30
|
+
CONFIGURATION_ERROR = "CONFIGURATION_ERROR"
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Custom error class for MCP-specific errors.
|
|
34
|
+
* Encapsulates a `BaseErrorCode`, a descriptive message, and optional details.
|
|
35
|
+
* Provides a method to format the error into a standard MCP tool response.
|
|
36
|
+
*/
|
|
37
|
+
export declare class McpError extends Error {
|
|
38
|
+
code: BaseErrorCode;
|
|
39
|
+
details?: Record<string, unknown> | undefined;
|
|
40
|
+
/**
|
|
41
|
+
* Creates an instance of McpError.
|
|
42
|
+
* @param {BaseErrorCode} code - The standardized error code.
|
|
43
|
+
* @param {string} message - A human-readable description of the error.
|
|
44
|
+
* @param {Record<string, unknown>} [details] - Optional additional details about the error.
|
|
45
|
+
*/
|
|
46
|
+
constructor(code: BaseErrorCode, message: string, details?: Record<string, unknown> | undefined);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Zod schema for validating error objects, potentially used for parsing
|
|
50
|
+
* error responses or validating error structures internally.
|
|
51
|
+
*/
|
|
52
|
+
export declare const ErrorSchema: z.ZodObject<{
|
|
53
|
+
/** The error code, corresponding to BaseErrorCode enum values. */
|
|
54
|
+
code: z.ZodNativeEnum<typeof BaseErrorCode>;
|
|
55
|
+
/** A human-readable description of the error. */
|
|
56
|
+
message: z.ZodString;
|
|
57
|
+
/** Optional additional details or context about the error. */
|
|
58
|
+
details: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
59
|
+
}, "strip", z.ZodTypeAny, {
|
|
60
|
+
code: BaseErrorCode;
|
|
61
|
+
message: string;
|
|
62
|
+
details?: Record<string, unknown> | undefined;
|
|
63
|
+
}, {
|
|
64
|
+
code: BaseErrorCode;
|
|
65
|
+
message: string;
|
|
66
|
+
details?: Record<string, unknown> | undefined;
|
|
67
|
+
}>;
|
|
68
|
+
/**
|
|
69
|
+
* TypeScript type inferred from `ErrorSchema`.
|
|
70
|
+
* Represents a validated error object structure.
|
|
71
|
+
* @typedef {z.infer<typeof ErrorSchema>} ErrorResponse
|
|
72
|
+
*/
|
|
73
|
+
export type ErrorResponse = z.infer<typeof ErrorSchema>;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Defines a set of standardized error codes for common issues within MCP servers or tools.
|
|
4
|
+
* These codes help clients understand the nature of an error programmatically.
|
|
5
|
+
*/
|
|
6
|
+
export var BaseErrorCode;
|
|
7
|
+
(function (BaseErrorCode) {
|
|
8
|
+
/** Access denied due to invalid credentials or lack of authentication. */
|
|
9
|
+
BaseErrorCode["UNAUTHORIZED"] = "UNAUTHORIZED";
|
|
10
|
+
/** Access denied despite valid authentication, due to insufficient permissions. */
|
|
11
|
+
BaseErrorCode["FORBIDDEN"] = "FORBIDDEN";
|
|
12
|
+
/** The requested resource or entity could not be found. */
|
|
13
|
+
BaseErrorCode["NOT_FOUND"] = "NOT_FOUND";
|
|
14
|
+
/** The request could not be completed due to a conflict with the current state of the resource. */
|
|
15
|
+
BaseErrorCode["CONFLICT"] = "CONFLICT";
|
|
16
|
+
/** The request failed due to invalid input parameters or data. */
|
|
17
|
+
BaseErrorCode["VALIDATION_ERROR"] = "VALIDATION_ERROR";
|
|
18
|
+
/** An error occurred while parsing input data (e.g., date string, JSON). */
|
|
19
|
+
BaseErrorCode["PARSING_ERROR"] = "PARSING_ERROR";
|
|
20
|
+
/** The request was rejected because the client has exceeded rate limits. */
|
|
21
|
+
BaseErrorCode["RATE_LIMITED"] = "RATE_LIMITED";
|
|
22
|
+
/** The request timed out before a response could be generated. */
|
|
23
|
+
BaseErrorCode["TIMEOUT"] = "TIMEOUT";
|
|
24
|
+
/** The service is temporarily unavailable, possibly due to maintenance or overload. */
|
|
25
|
+
BaseErrorCode["SERVICE_UNAVAILABLE"] = "SERVICE_UNAVAILABLE";
|
|
26
|
+
/** An unexpected error occurred on the server side. */
|
|
27
|
+
BaseErrorCode["INTERNAL_ERROR"] = "INTERNAL_ERROR";
|
|
28
|
+
/** An error occurred, but the specific cause is unknown or cannot be categorized. */
|
|
29
|
+
BaseErrorCode["UNKNOWN_ERROR"] = "UNKNOWN_ERROR";
|
|
30
|
+
/** An error occurred during the loading or validation of configuration data. */
|
|
31
|
+
BaseErrorCode["CONFIGURATION_ERROR"] = "CONFIGURATION_ERROR";
|
|
32
|
+
})(BaseErrorCode || (BaseErrorCode = {}));
|
|
33
|
+
/**
|
|
34
|
+
* Custom error class for MCP-specific errors.
|
|
35
|
+
* Encapsulates a `BaseErrorCode`, a descriptive message, and optional details.
|
|
36
|
+
* Provides a method to format the error into a standard MCP tool response.
|
|
37
|
+
*/
|
|
38
|
+
export class McpError extends Error {
|
|
39
|
+
/**
|
|
40
|
+
* Creates an instance of McpError.
|
|
41
|
+
* @param {BaseErrorCode} code - The standardized error code.
|
|
42
|
+
* @param {string} message - A human-readable description of the error.
|
|
43
|
+
* @param {Record<string, unknown>} [details] - Optional additional details about the error.
|
|
44
|
+
*/
|
|
45
|
+
constructor(code, message, details) {
|
|
46
|
+
super(message);
|
|
47
|
+
this.code = code;
|
|
48
|
+
this.details = details;
|
|
49
|
+
// Set the error name for identification
|
|
50
|
+
this.name = 'McpError';
|
|
51
|
+
// Ensure the prototype chain is correct
|
|
52
|
+
Object.setPrototypeOf(this, McpError.prototype);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Zod schema for validating error objects, potentially used for parsing
|
|
57
|
+
* error responses or validating error structures internally.
|
|
58
|
+
*/
|
|
59
|
+
export const ErrorSchema = z.object({
|
|
60
|
+
/** The error code, corresponding to BaseErrorCode enum values. */
|
|
61
|
+
code: z.nativeEnum(BaseErrorCode).describe("Standardized error code"),
|
|
62
|
+
/** A human-readable description of the error. */
|
|
63
|
+
message: z.string().describe("Detailed error message"),
|
|
64
|
+
/** Optional additional details or context about the error. */
|
|
65
|
+
details: z.record(z.unknown()).optional().describe("Optional structured error details")
|
|
66
|
+
}).describe("Schema for validating structured error objects.");
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Re-export all utilities from their categorized subdirectories
|
|
2
|
+
export * from './internal/index.js';
|
|
3
|
+
export * from './parsing/index.js';
|
|
4
|
+
export * from './security/index.js';
|
|
5
|
+
export * from './metrics/index.js';
|
|
6
|
+
// It's good practice to have index.ts files in each subdirectory
|
|
7
|
+
// that export the contents of that directory.
|
|
8
|
+
// Assuming those will be created or already exist.
|
|
9
|
+
// If not, this might need adjustment to export specific files, e.g.:
|
|
10
|
+
// export * from './internal/errorHandler.js';
|
|
11
|
+
// export * from './internal/logger.js';
|
|
12
|
+
// ... etc.
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { BaseErrorCode } 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
|
+
* Format an error for consistent response structure
|
|
79
|
+
* @param error The error to format
|
|
80
|
+
* @returns Formatted error object
|
|
81
|
+
*/
|
|
82
|
+
static formatError(error: unknown): Record<string, unknown>;
|
|
83
|
+
/**
|
|
84
|
+
* Safely execute a function and handle any errors
|
|
85
|
+
* @param fn Function to execute
|
|
86
|
+
* @param options Error handling options
|
|
87
|
+
* @returns The result of the function or error
|
|
88
|
+
*/
|
|
89
|
+
static tryCatch<T>(fn: () => Promise<T> | T, options: ErrorHandlerOptions): Promise<T>;
|
|
90
|
+
}
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import { BaseErrorCode, McpError } from '../../types-global/errors.js'; // Corrected path
|
|
2
|
+
import { logger } from './logger.js';
|
|
3
|
+
import { sanitizeInputForLogging } from '../index.js'; // Import from main barrel file
|
|
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
|
+
// Ensure details is an object before spreading
|
|
119
|
+
const existingDetails = typeof error.details === 'object' && error.details !== null ? error.details : {};
|
|
120
|
+
error.details = { ...existingDetails, ...context };
|
|
121
|
+
}
|
|
122
|
+
// Log the error with sanitized input
|
|
123
|
+
logger.error(`Error ${operation}: ${error.message}`, {
|
|
124
|
+
errorCode: error.code,
|
|
125
|
+
requestId: context?.requestId,
|
|
126
|
+
input: input ? sanitizeInputForLogging(input) : undefined,
|
|
127
|
+
stack: includeStack ? error.stack : undefined,
|
|
128
|
+
critical,
|
|
129
|
+
...context
|
|
130
|
+
});
|
|
131
|
+
if (rethrow) {
|
|
132
|
+
throw error;
|
|
133
|
+
}
|
|
134
|
+
// Ensure the function returns an Error type
|
|
135
|
+
return error;
|
|
136
|
+
}
|
|
137
|
+
// Sanitize input for logging
|
|
138
|
+
const sanitizedInput = input ? sanitizeInputForLogging(input) : undefined;
|
|
139
|
+
// Log the error with consistent format
|
|
140
|
+
logger.error(`Error ${operation}`, {
|
|
141
|
+
error: getErrorMessage(error), // Use helper function
|
|
142
|
+
errorType: getErrorName(error),
|
|
143
|
+
input: sanitizedInput,
|
|
144
|
+
requestId: context?.requestId,
|
|
145
|
+
stack: includeStack && error instanceof Error ? error.stack : undefined,
|
|
146
|
+
critical,
|
|
147
|
+
...context
|
|
148
|
+
});
|
|
149
|
+
// Choose the error code (explicit > determined > default)
|
|
150
|
+
const errorCode = explicitErrorCode ||
|
|
151
|
+
ErrorHandler.determineErrorCode(error) ||
|
|
152
|
+
BaseErrorCode.INTERNAL_ERROR;
|
|
153
|
+
// Transform to appropriate error type
|
|
154
|
+
let transformedError;
|
|
155
|
+
if (options.errorMapper) {
|
|
156
|
+
transformedError = options.errorMapper(error);
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
transformedError = new McpError(errorCode, `Error ${operation}: ${getErrorMessage(error)}`, // Use helper function
|
|
160
|
+
{
|
|
161
|
+
originalError: getErrorName(error),
|
|
162
|
+
...context
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
// Rethrow if requested
|
|
166
|
+
if (rethrow) {
|
|
167
|
+
throw transformedError;
|
|
168
|
+
}
|
|
169
|
+
// Ensure the function returns an Error type
|
|
170
|
+
return transformedError;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Map an error to a specific error type based on error message patterns
|
|
174
|
+
* @param error The error to map
|
|
175
|
+
* @param mappings Array of pattern and factory mappings
|
|
176
|
+
* @param defaultFactory Default factory function if no pattern matches
|
|
177
|
+
* @returns The mapped error
|
|
178
|
+
*/
|
|
179
|
+
static mapError(error, mappings, defaultFactory) {
|
|
180
|
+
// If it's already the target type and we have a default factory to check against, return it
|
|
181
|
+
if (defaultFactory && error instanceof Error) {
|
|
182
|
+
const defaultInstance = defaultFactory(error);
|
|
183
|
+
if (error.constructor === defaultInstance.constructor) {
|
|
184
|
+
return error;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
const errorMessage = getErrorMessage(error);
|
|
188
|
+
// Check each pattern and return the first match
|
|
189
|
+
for (const mapping of mappings) {
|
|
190
|
+
const matches = mapping.pattern instanceof RegExp
|
|
191
|
+
? mapping.pattern.test(errorMessage)
|
|
192
|
+
: errorMessage.includes(mapping.pattern);
|
|
193
|
+
if (matches) {
|
|
194
|
+
return mapping.factory(error, mapping.additionalContext);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
// Return default or original error
|
|
198
|
+
if (defaultFactory) {
|
|
199
|
+
return defaultFactory(error);
|
|
200
|
+
}
|
|
201
|
+
return error instanceof Error
|
|
202
|
+
? error
|
|
203
|
+
: new Error(String(error));
|
|
204
|
+
}
|
|
205
|
+
// Removed createErrorMapper method for simplification
|
|
206
|
+
/**
|
|
207
|
+
* Format an error for consistent response structure
|
|
208
|
+
* @param error The error to format
|
|
209
|
+
* @returns Formatted error object
|
|
210
|
+
*/
|
|
211
|
+
static formatError(error) {
|
|
212
|
+
if (error instanceof McpError) {
|
|
213
|
+
return {
|
|
214
|
+
code: error.code,
|
|
215
|
+
message: error.message,
|
|
216
|
+
// Ensure details is an object
|
|
217
|
+
details: typeof error.details === 'object' && error.details !== null ? error.details : {}
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
if (error instanceof Error) {
|
|
221
|
+
return {
|
|
222
|
+
code: ErrorHandler.determineErrorCode(error),
|
|
223
|
+
message: error.message,
|
|
224
|
+
details: { errorType: error.name }
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
return {
|
|
228
|
+
code: BaseErrorCode.UNKNOWN_ERROR,
|
|
229
|
+
message: String(error),
|
|
230
|
+
details: { errorType: typeof error }
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Safely execute a function and handle any errors
|
|
235
|
+
* @param fn Function to execute
|
|
236
|
+
* @param options Error handling options
|
|
237
|
+
* @returns The result of the function or error
|
|
238
|
+
*/
|
|
239
|
+
static async tryCatch(fn, options) {
|
|
240
|
+
try {
|
|
241
|
+
return await fn();
|
|
242
|
+
}
|
|
243
|
+
catch (error) {
|
|
244
|
+
throw ErrorHandler.handleError(error, { ...options, rethrow: true });
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Supported logging levels based on RFC 5424 Syslog severity levels used by MCP.
|
|
3
|
+
* emerg: 0, alert: 1, crit: 2, error: 3, warning: 4, notice: 5, info: 6, debug: 7
|
|
4
|
+
*/
|
|
5
|
+
export type McpLogLevel = 'debug' | 'info' | 'notice' | 'warning' | 'error' | 'crit' | 'alert' | 'emerg';
|
|
6
|
+
export type McpNotificationSender = (level: McpLogLevel, data: any, loggerName?: string) => void;
|
|
7
|
+
/**
|
|
8
|
+
* Singleton Logger wrapping Winston, adapted for MCP.
|
|
9
|
+
* Logs to files and optionally sends MCP notifications/message.
|
|
10
|
+
*/
|
|
11
|
+
declare class Logger {
|
|
12
|
+
private static instance;
|
|
13
|
+
private winstonLogger?;
|
|
14
|
+
private initialized;
|
|
15
|
+
private mcpNotificationSender?;
|
|
16
|
+
private currentMcpLevel;
|
|
17
|
+
private currentWinstonLevel;
|
|
18
|
+
private constructor();
|
|
19
|
+
/**
|
|
20
|
+
* Initialize Winston logger for file transport. Must be called once at app start.
|
|
21
|
+
* Console transport is added conditionally.
|
|
22
|
+
* @param level Initial minimum level to log ('info' default).
|
|
23
|
+
*/
|
|
24
|
+
initialize(level?: McpLogLevel): Promise<void>;
|
|
25
|
+
/**
|
|
26
|
+
* Sets the function used to send MCP 'notifications/message'.
|
|
27
|
+
*/
|
|
28
|
+
setMcpNotificationSender(sender: McpNotificationSender | undefined): void;
|
|
29
|
+
/**
|
|
30
|
+
* Dynamically sets the minimum logging level.
|
|
31
|
+
*/
|
|
32
|
+
setLevel(newLevel: McpLogLevel): void;
|
|
33
|
+
/** Get singleton instance. */
|
|
34
|
+
static getInstance(): Logger;
|
|
35
|
+
/** Ensures the logger has been initialized. */
|
|
36
|
+
private ensureInitialized;
|
|
37
|
+
/** Centralized log processing */
|
|
38
|
+
private log;
|
|
39
|
+
debug(msg: string, context?: Record<string, any>): void;
|
|
40
|
+
info(msg: string, context?: Record<string, any>): void;
|
|
41
|
+
notice(msg: string, context?: Record<string, any>): void;
|
|
42
|
+
warning(msg: string, context?: Record<string, any>): void;
|
|
43
|
+
error(msg: string, err?: Error | Record<string, any>, context?: Record<string, any>): void;
|
|
44
|
+
crit(msg: string, err?: Error | Record<string, any>, context?: Record<string, any>): void;
|
|
45
|
+
alert(msg: string, err?: Error | Record<string, any>, context?: Record<string, any>): void;
|
|
46
|
+
emerg(msg: string, err?: Error | Record<string, any>, context?: Record<string, any>): void;
|
|
47
|
+
fatal(msg: string, context?: Record<string, any>, error?: Error): void;
|
|
48
|
+
}
|
|
49
|
+
export declare const logger: Logger;
|
|
50
|
+
export {};
|