@cyanheads/pubmed-mcp-server 1.0.12
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 +174 -0
- package/dist/config/index.d.ts +91 -0
- package/dist/config/index.js +324 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.js +190 -0
- package/dist/mcp-server/resources/echoResource/echoResourceLogic.d.ts +79 -0
- package/dist/mcp-server/resources/echoResource/echoResourceLogic.js +82 -0
- package/dist/mcp-server/resources/echoResource/index.d.ts +13 -0
- package/dist/mcp-server/resources/echoResource/index.js +13 -0
- package/dist/mcp-server/resources/echoResource/registration.d.ts +30 -0
- package/dist/mcp-server/resources/echoResource/registration.js +168 -0
- package/dist/mcp-server/server.d.ts +28 -0
- package/dist/mcp-server/server.js +159 -0
- package/dist/mcp-server/tools/fetchPubMedContent/index.d.ts +6 -0
- package/dist/mcp-server/tools/fetchPubMedContent/index.js +6 -0
- package/dist/mcp-server/tools/fetchPubMedContent/logic.d.ts +63 -0
- package/dist/mcp-server/tools/fetchPubMedContent/logic.js +518 -0
- package/dist/mcp-server/tools/fetchPubMedContent/registration.d.ts +10 -0
- package/dist/mcp-server/tools/fetchPubMedContent/registration.js +37 -0
- package/dist/mcp-server/tools/getPubMedArticleConnections/index.d.ts +6 -0
- package/dist/mcp-server/tools/getPubMedArticleConnections/index.js +6 -0
- package/dist/mcp-server/tools/getPubMedArticleConnections/logic/citationFormatter.d.ts +9 -0
- package/dist/mcp-server/tools/getPubMedArticleConnections/logic/citationFormatter.js +288 -0
- package/dist/mcp-server/tools/getPubMedArticleConnections/logic/elinkHandler.d.ts +9 -0
- package/dist/mcp-server/tools/getPubMedArticleConnections/logic/elinkHandler.js +208 -0
- package/dist/mcp-server/tools/getPubMedArticleConnections/logic/index.d.ts +15 -0
- package/dist/mcp-server/tools/getPubMedArticleConnections/logic/index.js +82 -0
- package/dist/mcp-server/tools/getPubMedArticleConnections/logic/types.d.ts +27 -0
- package/dist/mcp-server/tools/getPubMedArticleConnections/logic/types.js +5 -0
- package/dist/mcp-server/tools/getPubMedArticleConnections/logic.d.ts +6 -0
- package/dist/mcp-server/tools/getPubMedArticleConnections/logic.js +6 -0
- package/dist/mcp-server/tools/getPubMedArticleConnections/registration.d.ts +36 -0
- package/dist/mcp-server/tools/getPubMedArticleConnections/registration.js +76 -0
- package/dist/mcp-server/tools/pubmedResearchAgent/index.d.ts +6 -0
- package/dist/mcp-server/tools/pubmedResearchAgent/index.js +6 -0
- package/dist/mcp-server/tools/pubmedResearchAgent/logic/index.d.ts +7 -0
- package/dist/mcp-server/tools/pubmedResearchAgent/logic/index.js +9 -0
- package/dist/mcp-server/tools/pubmedResearchAgent/logic/inputSchema.d.ts +111 -0
- package/dist/mcp-server/tools/pubmedResearchAgent/logic/inputSchema.js +149 -0
- package/dist/mcp-server/tools/pubmedResearchAgent/logic/outputTypes.d.ts +106 -0
- package/dist/mcp-server/tools/pubmedResearchAgent/logic/outputTypes.js +7 -0
- package/dist/mcp-server/tools/pubmedResearchAgent/logic/planOrchestrator.d.ts +10 -0
- package/dist/mcp-server/tools/pubmedResearchAgent/logic/planOrchestrator.js +251 -0
- package/dist/mcp-server/tools/pubmedResearchAgent/logic.d.ts +12 -0
- package/dist/mcp-server/tools/pubmedResearchAgent/logic.js +55 -0
- package/dist/mcp-server/tools/pubmedResearchAgent/registration.d.ts +10 -0
- package/dist/mcp-server/tools/pubmedResearchAgent/registration.js +36 -0
- package/dist/mcp-server/tools/searchPubMedArticles/index.d.ts +6 -0
- package/dist/mcp-server/tools/searchPubMedArticles/index.js +6 -0
- package/dist/mcp-server/tools/searchPubMedArticles/logic.d.ts +61 -0
- package/dist/mcp-server/tools/searchPubMedArticles/logic.js +229 -0
- package/dist/mcp-server/tools/searchPubMedArticles/registration.d.ts +10 -0
- package/dist/mcp-server/tools/searchPubMedArticles/registration.js +38 -0
- package/dist/mcp-server/transports/authentication/authMiddleware.d.ts +29 -0
- package/dist/mcp-server/transports/authentication/authMiddleware.js +174 -0
- package/dist/mcp-server/transports/httpTransport.d.ts +23 -0
- package/dist/mcp-server/transports/httpTransport.js +463 -0
- package/dist/mcp-server/transports/stdioTransport.d.ts +42 -0
- package/dist/mcp-server/transports/stdioTransport.js +63 -0
- package/dist/services/NCBI/ncbiConstants.d.ts +22 -0
- package/dist/services/NCBI/ncbiConstants.js +5 -0
- package/dist/services/NCBI/ncbiCoreApiClient.d.ts +24 -0
- package/dist/services/NCBI/ncbiCoreApiClient.js +118 -0
- package/dist/services/NCBI/ncbiRequestQueueManager.d.ts +36 -0
- package/dist/services/NCBI/ncbiRequestQueueManager.js +96 -0
- package/dist/services/NCBI/ncbiResponseHandler.d.ts +22 -0
- package/dist/services/NCBI/ncbiResponseHandler.js +192 -0
- package/dist/services/NCBI/ncbiService.d.ts +24 -0
- package/dist/services/NCBI/ncbiService.js +57 -0
- package/dist/services/index.d.ts +7 -0
- package/dist/services/index.js +7 -0
- package/dist/services/llm-providers/index.d.ts +7 -0
- package/dist/services/llm-providers/index.js +7 -0
- package/dist/services/llm-providers/llmFactory.d.ts +69 -0
- package/dist/services/llm-providers/llmFactory.js +132 -0
- package/dist/services/llm-providers/openRouter/index.d.ts +6 -0
- package/dist/services/llm-providers/openRouter/index.js +7 -0
- package/dist/services/llm-providers/openRouter/openRouterProvider.d.ts +99 -0
- package/dist/services/llm-providers/openRouter/openRouterProvider.js +329 -0
- package/dist/types-global/errors.d.ts +119 -0
- package/dist/types-global/errors.js +119 -0
- package/dist/types-global/pubmedXml.d.ts +350 -0
- package/dist/types-global/pubmedXml.js +7 -0
- package/dist/utils/index.d.ts +10 -0
- package/dist/utils/index.js +18 -0
- package/dist/utils/internal/errorHandler.d.ts +176 -0
- package/dist/utils/internal/errorHandler.js +335 -0
- package/dist/utils/internal/index.d.ts +9 -0
- package/dist/utils/internal/index.js +9 -0
- package/dist/utils/internal/logger.d.ts +141 -0
- package/dist/utils/internal/logger.js +426 -0
- package/dist/utils/internal/requestContext.d.ts +83 -0
- package/dist/utils/internal/requestContext.js +72 -0
- package/dist/utils/metrics/index.d.ts +7 -0
- package/dist/utils/metrics/index.js +7 -0
- package/dist/utils/metrics/tokenCounter.d.ts +35 -0
- package/dist/utils/metrics/tokenCounter.js +110 -0
- package/dist/utils/parsing/dateParser.d.ts +73 -0
- package/dist/utils/parsing/dateParser.js +107 -0
- package/dist/utils/parsing/index.d.ts +8 -0
- package/dist/utils/parsing/index.js +8 -0
- package/dist/utils/parsing/jsonParser.d.ts +82 -0
- package/dist/utils/parsing/jsonParser.js +126 -0
- package/dist/utils/parsing/ncbi-parsing/eSummaryResultParser.d.ts +32 -0
- package/dist/utils/parsing/ncbi-parsing/eSummaryResultParser.js +295 -0
- package/dist/utils/parsing/ncbi-parsing/index.d.ts +8 -0
- package/dist/utils/parsing/ncbi-parsing/index.js +8 -0
- package/dist/utils/parsing/ncbi-parsing/pubmedArticleStructureParser.d.ts +70 -0
- package/dist/utils/parsing/ncbi-parsing/pubmedArticleStructureParser.js +229 -0
- package/dist/utils/parsing/ncbi-parsing/xmlGenericHelpers.d.ts +31 -0
- package/dist/utils/parsing/ncbi-parsing/xmlGenericHelpers.js +76 -0
- package/dist/utils/security/idGenerator.d.ts +134 -0
- package/dist/utils/security/idGenerator.js +198 -0
- package/dist/utils/security/index.d.ts +9 -0
- package/dist/utils/security/index.js +9 -0
- package/dist/utils/security/rateLimiter.d.ts +108 -0
- package/dist/utils/security/rateLimiter.js +179 -0
- package/dist/utils/security/sanitization.d.ts +173 -0
- package/dist/utils/security/sanitization.js +439 -0
- package/package.json +97 -0
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview This module provides utilities for robust error handling.
|
|
3
|
+
* It defines structures for error context, options for handling errors,
|
|
4
|
+
* and mappings for classifying errors. The main `ErrorHandler` class
|
|
5
|
+
* offers static methods for consistent error processing, logging, and transformation.
|
|
6
|
+
* @module src/utils/internal/errorHandler
|
|
7
|
+
*/
|
|
8
|
+
import { BaseErrorCode } from "../../types-global/errors.js";
|
|
9
|
+
/**
|
|
10
|
+
* Defines a generic structure for providing context with errors.
|
|
11
|
+
* This context can include identifiers like `requestId` or any other relevant
|
|
12
|
+
* key-value pairs that aid in debugging or understanding the error's circumstances.
|
|
13
|
+
*/
|
|
14
|
+
export interface ErrorContext {
|
|
15
|
+
/**
|
|
16
|
+
* A unique identifier for the request or operation during which the error occurred.
|
|
17
|
+
* Useful for tracing errors through logs and distributed systems.
|
|
18
|
+
*/
|
|
19
|
+
requestId?: string;
|
|
20
|
+
/**
|
|
21
|
+
* Allows for arbitrary additional context information.
|
|
22
|
+
* Keys are strings, and values can be of any type.
|
|
23
|
+
*/
|
|
24
|
+
[key: string]: unknown;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Configuration options for the `ErrorHandler.handleError` method.
|
|
28
|
+
* These options control how an error is processed, logged, and whether it's rethrown.
|
|
29
|
+
*/
|
|
30
|
+
export interface ErrorHandlerOptions {
|
|
31
|
+
/**
|
|
32
|
+
* The context of the operation that caused the error.
|
|
33
|
+
* This can include `requestId` and other relevant debugging information.
|
|
34
|
+
*/
|
|
35
|
+
context?: ErrorContext;
|
|
36
|
+
/**
|
|
37
|
+
* A descriptive name of the operation being performed when the error occurred.
|
|
38
|
+
* This helps in identifying the source or nature of the error in logs.
|
|
39
|
+
* Example: "UserLogin", "ProcessPayment", "FetchUserProfile".
|
|
40
|
+
*/
|
|
41
|
+
operation: string;
|
|
42
|
+
/**
|
|
43
|
+
* The input data or parameters that were being processed when the error occurred.
|
|
44
|
+
* This input will be sanitized before logging to prevent sensitive data exposure.
|
|
45
|
+
*/
|
|
46
|
+
input?: unknown;
|
|
47
|
+
/**
|
|
48
|
+
* If true, the (potentially transformed) error will be rethrown after handling.
|
|
49
|
+
* Defaults to `false`.
|
|
50
|
+
*/
|
|
51
|
+
rethrow?: boolean;
|
|
52
|
+
/**
|
|
53
|
+
* A specific `BaseErrorCode` to assign to the error, overriding any
|
|
54
|
+
* automatically determined error code.
|
|
55
|
+
*/
|
|
56
|
+
errorCode?: BaseErrorCode;
|
|
57
|
+
/**
|
|
58
|
+
* A custom function to map or transform the original error into a new `Error` instance.
|
|
59
|
+
* If provided, this function is used instead of the default `McpError` creation.
|
|
60
|
+
* @param error - The original error that occurred.
|
|
61
|
+
* @returns The transformed error.
|
|
62
|
+
*/
|
|
63
|
+
errorMapper?: (error: unknown) => Error;
|
|
64
|
+
/**
|
|
65
|
+
* If true, stack traces will be included in the logs.
|
|
66
|
+
* Defaults to `true`.
|
|
67
|
+
*/
|
|
68
|
+
includeStack?: boolean;
|
|
69
|
+
/**
|
|
70
|
+
* If true, indicates that the error is critical and might require immediate attention
|
|
71
|
+
* or could lead to system instability. This is primarily for logging and alerting.
|
|
72
|
+
* Defaults to `false`.
|
|
73
|
+
*/
|
|
74
|
+
critical?: boolean;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Defines a basic rule for mapping errors based on patterns.
|
|
78
|
+
* Used internally by `COMMON_ERROR_PATTERNS` and as a base for `ErrorMapping`.
|
|
79
|
+
*/
|
|
80
|
+
export interface BaseErrorMapping {
|
|
81
|
+
/**
|
|
82
|
+
* A string or regular expression to match against the error message.
|
|
83
|
+
* If a string is provided, it's typically used for substring matching (case-insensitive).
|
|
84
|
+
*/
|
|
85
|
+
pattern: string | RegExp;
|
|
86
|
+
/**
|
|
87
|
+
* The `BaseErrorCode` to assign if the pattern matches.
|
|
88
|
+
*/
|
|
89
|
+
errorCode: BaseErrorCode;
|
|
90
|
+
/**
|
|
91
|
+
* An optional custom message template for the mapped error.
|
|
92
|
+
* (Note: This property is defined but not directly used by `ErrorHandler.determineErrorCode`
|
|
93
|
+
* which focuses on `errorCode`. It's more relevant for custom mapping logic.)
|
|
94
|
+
*/
|
|
95
|
+
messageTemplate?: string;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Extends `BaseErrorMapping` to include a factory function for creating
|
|
99
|
+
* specific error instances and additional context for the mapping.
|
|
100
|
+
* Used by `ErrorHandler.mapError`.
|
|
101
|
+
* @template T The type of `Error` this mapping will produce, defaults to `Error`.
|
|
102
|
+
*/
|
|
103
|
+
export interface ErrorMapping<T extends Error = Error> extends BaseErrorMapping {
|
|
104
|
+
/**
|
|
105
|
+
* A factory function that creates and returns an instance of the mapped error type `T`.
|
|
106
|
+
* @param error - The original error that occurred.
|
|
107
|
+
* @param context - Optional additional context provided in the mapping rule.
|
|
108
|
+
* @returns The newly created error instance.
|
|
109
|
+
*/
|
|
110
|
+
factory: (error: unknown, context?: Record<string, unknown>) => T;
|
|
111
|
+
/**
|
|
112
|
+
* Additional static context to be merged or passed to the `factory` function
|
|
113
|
+
* when this mapping rule is applied.
|
|
114
|
+
*/
|
|
115
|
+
additionalContext?: Record<string, unknown>;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* A utility class providing static methods for comprehensive error handling.
|
|
119
|
+
*/
|
|
120
|
+
export declare class ErrorHandler {
|
|
121
|
+
/**
|
|
122
|
+
* Determines an appropriate `BaseErrorCode` for a given error.
|
|
123
|
+
* Checks `McpError` instances, `ERROR_TYPE_MAPPINGS`, and `COMMON_ERROR_PATTERNS`.
|
|
124
|
+
* Defaults to `BaseErrorCode.INTERNAL_ERROR`.
|
|
125
|
+
* @param error - The error instance or value to classify.
|
|
126
|
+
* @returns The determined error code.
|
|
127
|
+
*/
|
|
128
|
+
static determineErrorCode(error: unknown): BaseErrorCode;
|
|
129
|
+
/**
|
|
130
|
+
* Handles an error with consistent logging and optional transformation.
|
|
131
|
+
* Sanitizes input, determines error code, logs details, and can rethrow.
|
|
132
|
+
* @param error - The error instance or value that occurred.
|
|
133
|
+
* @param options - Configuration for handling the error.
|
|
134
|
+
* @returns The handled (and potentially transformed) error instance.
|
|
135
|
+
*/
|
|
136
|
+
static handleError(error: unknown, options: ErrorHandlerOptions): Error;
|
|
137
|
+
/**
|
|
138
|
+
* Maps an error to a specific error type `T` based on `ErrorMapping` rules.
|
|
139
|
+
* Returns original/default error if no mapping matches.
|
|
140
|
+
* @template T The target error type, extending `Error`.
|
|
141
|
+
* @param error - The error instance or value to map.
|
|
142
|
+
* @param mappings - An array of mapping rules to apply.
|
|
143
|
+
* @param defaultFactory - Optional factory for a default error if no mapping matches.
|
|
144
|
+
* @returns The mapped error of type `T`, or the original/defaulted error.
|
|
145
|
+
*/
|
|
146
|
+
static mapError<T extends Error>(error: unknown, mappings: ReadonlyArray<ErrorMapping<T>>, defaultFactory?: (error: unknown, context?: Record<string, unknown>) => T): T | Error;
|
|
147
|
+
/**
|
|
148
|
+
* Formats an error into a consistent object structure for API responses or structured logging.
|
|
149
|
+
* @param error - The error instance or value to format.
|
|
150
|
+
* @returns A structured representation of the error.
|
|
151
|
+
*/
|
|
152
|
+
static formatError(error: unknown): Record<string, unknown>;
|
|
153
|
+
/**
|
|
154
|
+
* Safely executes a function (sync or async) and handles errors using `ErrorHandler.handleError`.
|
|
155
|
+
* The error is always rethrown.
|
|
156
|
+
* @template T The expected return type of the function `fn`.
|
|
157
|
+
* @param fn - The function to execute.
|
|
158
|
+
* @param options - Error handling options (excluding `rethrow`).
|
|
159
|
+
* @returns A promise resolving with the result of `fn` if successful.
|
|
160
|
+
* @throws {McpError | Error} The error processed by `ErrorHandler.handleError`.
|
|
161
|
+
* @example
|
|
162
|
+
* ```typescript
|
|
163
|
+
* async function fetchData(userId: string, context: RequestContext) {
|
|
164
|
+
* return ErrorHandler.tryCatch(
|
|
165
|
+
* async () => {
|
|
166
|
+
* const response = await fetch(`/api/users/${userId}`);
|
|
167
|
+
* if (!response.ok) throw new Error(`Failed to fetch user: ${response.status}`);
|
|
168
|
+
* return response.json();
|
|
169
|
+
* },
|
|
170
|
+
* { operation: 'fetchUserData', context, input: { userId } }
|
|
171
|
+
* );
|
|
172
|
+
* }
|
|
173
|
+
* ```
|
|
174
|
+
*/
|
|
175
|
+
static tryCatch<T>(fn: () => Promise<T> | T, options: Omit<ErrorHandlerOptions, "rethrow">): Promise<T>;
|
|
176
|
+
}
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview This module provides utilities for robust error handling.
|
|
3
|
+
* It defines structures for error context, options for handling errors,
|
|
4
|
+
* and mappings for classifying errors. The main `ErrorHandler` class
|
|
5
|
+
* offers static methods for consistent error processing, logging, and transformation.
|
|
6
|
+
* @module src/utils/internal/errorHandler
|
|
7
|
+
*/
|
|
8
|
+
import { BaseErrorCode, McpError } from "../../types-global/errors.js";
|
|
9
|
+
import { generateUUID, sanitizeInputForLogging } from "../index.js";
|
|
10
|
+
import { logger } from "./logger.js";
|
|
11
|
+
/**
|
|
12
|
+
* Maps standard JavaScript error constructor names to `BaseErrorCode` values.
|
|
13
|
+
* @private
|
|
14
|
+
*/
|
|
15
|
+
const ERROR_TYPE_MAPPINGS = {
|
|
16
|
+
SyntaxError: BaseErrorCode.VALIDATION_ERROR,
|
|
17
|
+
TypeError: BaseErrorCode.VALIDATION_ERROR,
|
|
18
|
+
ReferenceError: BaseErrorCode.INTERNAL_ERROR,
|
|
19
|
+
RangeError: BaseErrorCode.VALIDATION_ERROR,
|
|
20
|
+
URIError: BaseErrorCode.VALIDATION_ERROR,
|
|
21
|
+
EvalError: BaseErrorCode.INTERNAL_ERROR,
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Array of `BaseErrorMapping` rules to classify errors by message/name patterns.
|
|
25
|
+
* Order matters: more specific patterns should precede generic ones.
|
|
26
|
+
* @private
|
|
27
|
+
*/
|
|
28
|
+
const COMMON_ERROR_PATTERNS = [
|
|
29
|
+
{
|
|
30
|
+
pattern: /auth|unauthorized|unauthenticated|not.*logged.*in|invalid.*token|expired.*token/i,
|
|
31
|
+
errorCode: BaseErrorCode.UNAUTHORIZED,
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
pattern: /permission|forbidden|access.*denied|not.*allowed/i,
|
|
35
|
+
errorCode: BaseErrorCode.FORBIDDEN,
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
pattern: /not found|missing|no such|doesn't exist|couldn't find/i,
|
|
39
|
+
errorCode: BaseErrorCode.NOT_FOUND,
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
pattern: /invalid|validation|malformed|bad request|wrong format|missing required/i,
|
|
43
|
+
errorCode: BaseErrorCode.VALIDATION_ERROR,
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
pattern: /conflict|already exists|duplicate|unique constraint/i,
|
|
47
|
+
errorCode: BaseErrorCode.CONFLICT,
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
pattern: /rate limit|too many requests|throttled/i,
|
|
51
|
+
errorCode: BaseErrorCode.RATE_LIMITED,
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
pattern: /timeout|timed out|deadline exceeded/i,
|
|
55
|
+
errorCode: BaseErrorCode.TIMEOUT,
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
pattern: /service unavailable|bad gateway|gateway timeout|upstream error/i,
|
|
59
|
+
errorCode: BaseErrorCode.SERVICE_UNAVAILABLE,
|
|
60
|
+
},
|
|
61
|
+
];
|
|
62
|
+
/**
|
|
63
|
+
* Creates a "safe" RegExp for testing error messages.
|
|
64
|
+
* Ensures case-insensitivity and removes the global flag.
|
|
65
|
+
* @param pattern - The string or RegExp pattern.
|
|
66
|
+
* @returns A new RegExp instance.
|
|
67
|
+
* @private
|
|
68
|
+
*/
|
|
69
|
+
function createSafeRegex(pattern) {
|
|
70
|
+
if (pattern instanceof RegExp) {
|
|
71
|
+
let flags = pattern.flags.replace("g", "");
|
|
72
|
+
if (!flags.includes("i")) {
|
|
73
|
+
flags += "i";
|
|
74
|
+
}
|
|
75
|
+
return new RegExp(pattern.source, flags);
|
|
76
|
+
}
|
|
77
|
+
return new RegExp(pattern, "i");
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Retrieves a descriptive name for an error object or value.
|
|
81
|
+
* @param error - The error object or value.
|
|
82
|
+
* @returns A string representing the error's name or type.
|
|
83
|
+
* @private
|
|
84
|
+
*/
|
|
85
|
+
function getErrorName(error) {
|
|
86
|
+
if (error instanceof Error) {
|
|
87
|
+
return error.name || "Error";
|
|
88
|
+
}
|
|
89
|
+
if (error === null) {
|
|
90
|
+
return "NullValueEncountered";
|
|
91
|
+
}
|
|
92
|
+
if (error === undefined) {
|
|
93
|
+
return "UndefinedValueEncountered";
|
|
94
|
+
}
|
|
95
|
+
if (typeof error === "object" &&
|
|
96
|
+
error !== null &&
|
|
97
|
+
error.constructor &&
|
|
98
|
+
typeof error.constructor.name === "string" &&
|
|
99
|
+
error.constructor.name !== "Object") {
|
|
100
|
+
return `${error.constructor.name}Encountered`;
|
|
101
|
+
}
|
|
102
|
+
return `${typeof error}Encountered`;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Extracts a message string from an error object or value.
|
|
106
|
+
* @param error - The error object or value.
|
|
107
|
+
* @returns The error message string.
|
|
108
|
+
* @private
|
|
109
|
+
*/
|
|
110
|
+
function getErrorMessage(error) {
|
|
111
|
+
if (error instanceof Error) {
|
|
112
|
+
return error.message;
|
|
113
|
+
}
|
|
114
|
+
if (error === null) {
|
|
115
|
+
return "Null value encountered as error";
|
|
116
|
+
}
|
|
117
|
+
if (error === undefined) {
|
|
118
|
+
return "Undefined value encountered as error";
|
|
119
|
+
}
|
|
120
|
+
if (typeof error === "string") {
|
|
121
|
+
return error;
|
|
122
|
+
}
|
|
123
|
+
try {
|
|
124
|
+
const str = String(error);
|
|
125
|
+
if (str === "[object Object]" && error !== null) {
|
|
126
|
+
try {
|
|
127
|
+
return `Non-Error object encountered: ${JSON.stringify(error)}`;
|
|
128
|
+
}
|
|
129
|
+
catch (stringifyError) {
|
|
130
|
+
return `Unstringifyable non-Error object encountered (constructor: ${error.constructor?.name || "Unknown"})`;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return str;
|
|
134
|
+
}
|
|
135
|
+
catch (e) {
|
|
136
|
+
return `Error converting error to string: ${e instanceof Error ? e.message : "Unknown conversion error"}`;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* A utility class providing static methods for comprehensive error handling.
|
|
141
|
+
*/
|
|
142
|
+
export class ErrorHandler {
|
|
143
|
+
/**
|
|
144
|
+
* Determines an appropriate `BaseErrorCode` for a given error.
|
|
145
|
+
* Checks `McpError` instances, `ERROR_TYPE_MAPPINGS`, and `COMMON_ERROR_PATTERNS`.
|
|
146
|
+
* Defaults to `BaseErrorCode.INTERNAL_ERROR`.
|
|
147
|
+
* @param error - The error instance or value to classify.
|
|
148
|
+
* @returns The determined error code.
|
|
149
|
+
*/
|
|
150
|
+
static determineErrorCode(error) {
|
|
151
|
+
if (error instanceof McpError) {
|
|
152
|
+
return error.code;
|
|
153
|
+
}
|
|
154
|
+
const errorName = getErrorName(error);
|
|
155
|
+
const errorMessage = getErrorMessage(error);
|
|
156
|
+
if (errorName in ERROR_TYPE_MAPPINGS) {
|
|
157
|
+
return ERROR_TYPE_MAPPINGS[errorName];
|
|
158
|
+
}
|
|
159
|
+
for (const mapping of COMMON_ERROR_PATTERNS) {
|
|
160
|
+
const regex = createSafeRegex(mapping.pattern);
|
|
161
|
+
if (regex.test(errorMessage) || regex.test(errorName)) {
|
|
162
|
+
return mapping.errorCode;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return BaseErrorCode.INTERNAL_ERROR;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Handles an error with consistent logging and optional transformation.
|
|
169
|
+
* Sanitizes input, determines error code, logs details, and can rethrow.
|
|
170
|
+
* @param error - The error instance or value that occurred.
|
|
171
|
+
* @param options - Configuration for handling the error.
|
|
172
|
+
* @returns The handled (and potentially transformed) error instance.
|
|
173
|
+
*/
|
|
174
|
+
static handleError(error, options) {
|
|
175
|
+
const { context = {}, operation, input, rethrow = false, errorCode: explicitErrorCode, includeStack = true, critical = false, errorMapper, } = options;
|
|
176
|
+
const sanitizedInput = input !== undefined ? sanitizeInputForLogging(input) : undefined;
|
|
177
|
+
const originalErrorName = getErrorName(error);
|
|
178
|
+
const originalErrorMessage = getErrorMessage(error);
|
|
179
|
+
const originalStack = error instanceof Error ? error.stack : undefined;
|
|
180
|
+
let finalError;
|
|
181
|
+
let loggedErrorCode;
|
|
182
|
+
const errorDetailsSeed = error instanceof McpError &&
|
|
183
|
+
typeof error.details === "object" &&
|
|
184
|
+
error.details !== null
|
|
185
|
+
? { ...error.details }
|
|
186
|
+
: {};
|
|
187
|
+
const consolidatedDetails = {
|
|
188
|
+
...errorDetailsSeed,
|
|
189
|
+
...context,
|
|
190
|
+
originalErrorName,
|
|
191
|
+
originalMessage: originalErrorMessage,
|
|
192
|
+
};
|
|
193
|
+
if (originalStack &&
|
|
194
|
+
!(error instanceof McpError && error.details?.originalStack)) {
|
|
195
|
+
consolidatedDetails.originalStack = originalStack;
|
|
196
|
+
}
|
|
197
|
+
if (error instanceof McpError) {
|
|
198
|
+
loggedErrorCode = error.code;
|
|
199
|
+
finalError = errorMapper
|
|
200
|
+
? errorMapper(error)
|
|
201
|
+
: new McpError(error.code, error.message, consolidatedDetails);
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
loggedErrorCode =
|
|
205
|
+
explicitErrorCode || ErrorHandler.determineErrorCode(error);
|
|
206
|
+
const message = `Error in ${operation}: ${originalErrorMessage}`;
|
|
207
|
+
finalError = errorMapper
|
|
208
|
+
? errorMapper(error)
|
|
209
|
+
: new McpError(loggedErrorCode, message, consolidatedDetails);
|
|
210
|
+
}
|
|
211
|
+
if (finalError !== error &&
|
|
212
|
+
error instanceof Error &&
|
|
213
|
+
finalError instanceof Error &&
|
|
214
|
+
!finalError.stack &&
|
|
215
|
+
error.stack) {
|
|
216
|
+
finalError.stack = error.stack;
|
|
217
|
+
}
|
|
218
|
+
const logRequestId = typeof context.requestId === "string" && context.requestId
|
|
219
|
+
? context.requestId
|
|
220
|
+
: generateUUID();
|
|
221
|
+
const logTimestamp = typeof context.timestamp === "string" && context.timestamp
|
|
222
|
+
? context.timestamp
|
|
223
|
+
: new Date().toISOString();
|
|
224
|
+
const logPayload = {
|
|
225
|
+
requestId: logRequestId,
|
|
226
|
+
timestamp: logTimestamp,
|
|
227
|
+
operation,
|
|
228
|
+
input: sanitizedInput,
|
|
229
|
+
critical,
|
|
230
|
+
errorCode: loggedErrorCode,
|
|
231
|
+
originalErrorType: originalErrorName,
|
|
232
|
+
finalErrorType: getErrorName(finalError),
|
|
233
|
+
...Object.fromEntries(Object.entries(context).filter(([key]) => key !== "requestId" && key !== "timestamp")),
|
|
234
|
+
};
|
|
235
|
+
if (finalError instanceof McpError && finalError.details) {
|
|
236
|
+
logPayload.errorDetails = finalError.details;
|
|
237
|
+
}
|
|
238
|
+
else {
|
|
239
|
+
logPayload.errorDetails = consolidatedDetails;
|
|
240
|
+
}
|
|
241
|
+
if (includeStack) {
|
|
242
|
+
const stack = finalError instanceof Error ? finalError.stack : originalStack;
|
|
243
|
+
if (stack) {
|
|
244
|
+
logPayload.stack = stack;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
logger.error(`Error in ${operation}: ${finalError.message || originalErrorMessage}`, logPayload);
|
|
248
|
+
if (rethrow) {
|
|
249
|
+
throw finalError;
|
|
250
|
+
}
|
|
251
|
+
return finalError;
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Maps an error to a specific error type `T` based on `ErrorMapping` rules.
|
|
255
|
+
* Returns original/default error if no mapping matches.
|
|
256
|
+
* @template T The target error type, extending `Error`.
|
|
257
|
+
* @param error - The error instance or value to map.
|
|
258
|
+
* @param mappings - An array of mapping rules to apply.
|
|
259
|
+
* @param defaultFactory - Optional factory for a default error if no mapping matches.
|
|
260
|
+
* @returns The mapped error of type `T`, or the original/defaulted error.
|
|
261
|
+
*/
|
|
262
|
+
static mapError(error, mappings, defaultFactory) {
|
|
263
|
+
const errorMessage = getErrorMessage(error);
|
|
264
|
+
const errorName = getErrorName(error);
|
|
265
|
+
for (const mapping of mappings) {
|
|
266
|
+
const regex = createSafeRegex(mapping.pattern);
|
|
267
|
+
if (regex.test(errorMessage) || regex.test(errorName)) {
|
|
268
|
+
return mapping.factory(error, mapping.additionalContext);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
if (defaultFactory) {
|
|
272
|
+
return defaultFactory(error);
|
|
273
|
+
}
|
|
274
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Formats an error into a consistent object structure for API responses or structured logging.
|
|
278
|
+
* @param error - The error instance or value to format.
|
|
279
|
+
* @returns A structured representation of the error.
|
|
280
|
+
*/
|
|
281
|
+
static formatError(error) {
|
|
282
|
+
if (error instanceof McpError) {
|
|
283
|
+
return {
|
|
284
|
+
code: error.code,
|
|
285
|
+
message: error.message,
|
|
286
|
+
details: typeof error.details === "object" && error.details !== null
|
|
287
|
+
? error.details
|
|
288
|
+
: {},
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
if (error instanceof Error) {
|
|
292
|
+
return {
|
|
293
|
+
code: ErrorHandler.determineErrorCode(error),
|
|
294
|
+
message: error.message,
|
|
295
|
+
details: { errorType: error.name || "Error" },
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
return {
|
|
299
|
+
code: BaseErrorCode.UNKNOWN_ERROR,
|
|
300
|
+
message: getErrorMessage(error),
|
|
301
|
+
details: { errorType: getErrorName(error) },
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Safely executes a function (sync or async) and handles errors using `ErrorHandler.handleError`.
|
|
306
|
+
* The error is always rethrown.
|
|
307
|
+
* @template T The expected return type of the function `fn`.
|
|
308
|
+
* @param fn - The function to execute.
|
|
309
|
+
* @param options - Error handling options (excluding `rethrow`).
|
|
310
|
+
* @returns A promise resolving with the result of `fn` if successful.
|
|
311
|
+
* @throws {McpError | Error} The error processed by `ErrorHandler.handleError`.
|
|
312
|
+
* @example
|
|
313
|
+
* ```typescript
|
|
314
|
+
* async function fetchData(userId: string, context: RequestContext) {
|
|
315
|
+
* return ErrorHandler.tryCatch(
|
|
316
|
+
* async () => {
|
|
317
|
+
* const response = await fetch(`/api/users/${userId}`);
|
|
318
|
+
* if (!response.ok) throw new Error(`Failed to fetch user: ${response.status}`);
|
|
319
|
+
* return response.json();
|
|
320
|
+
* },
|
|
321
|
+
* { operation: 'fetchUserData', context, input: { userId } }
|
|
322
|
+
* );
|
|
323
|
+
* }
|
|
324
|
+
* ```
|
|
325
|
+
*/
|
|
326
|
+
static async tryCatch(fn, options) {
|
|
327
|
+
try {
|
|
328
|
+
return await Promise.resolve(fn());
|
|
329
|
+
}
|
|
330
|
+
catch (error) {
|
|
331
|
+
// ErrorHandler.handleError will return the error to be thrown.
|
|
332
|
+
throw ErrorHandler.handleError(error, { ...options, rethrow: true });
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Barrel file for internal utility modules.
|
|
3
|
+
* This file re-exports core internal utilities related to error handling,
|
|
4
|
+
* logging, and request context management.
|
|
5
|
+
* @module src/utils/internal
|
|
6
|
+
*/
|
|
7
|
+
export * from "./errorHandler.js";
|
|
8
|
+
export * from "./logger.js";
|
|
9
|
+
export * from "./requestContext.js";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Barrel file for internal utility modules.
|
|
3
|
+
* This file re-exports core internal utilities related to error handling,
|
|
4
|
+
* logging, and request context management.
|
|
5
|
+
* @module src/utils/internal
|
|
6
|
+
*/
|
|
7
|
+
export * from "./errorHandler.js";
|
|
8
|
+
export * from "./logger.js";
|
|
9
|
+
export * from "./requestContext.js";
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { RequestContext } from "./requestContext.js";
|
|
2
|
+
/**
|
|
3
|
+
* Defines the supported logging levels based on RFC 5424 Syslog severity levels,
|
|
4
|
+
* as used by the Model Context Protocol (MCP).
|
|
5
|
+
* Levels are: 'debug'(7), 'info'(6), 'notice'(5), 'warning'(4), 'error'(3), 'crit'(2), 'alert'(1), 'emerg'(0).
|
|
6
|
+
* Lower numeric values indicate higher severity.
|
|
7
|
+
*/
|
|
8
|
+
export type McpLogLevel = "debug" | "info" | "notice" | "warning" | "error" | "crit" | "alert" | "emerg";
|
|
9
|
+
/**
|
|
10
|
+
* Interface for the payload of an MCP log notification.
|
|
11
|
+
* This structure is used when sending log data via MCP `notifications/message`.
|
|
12
|
+
*/
|
|
13
|
+
export interface McpLogPayload {
|
|
14
|
+
message: string;
|
|
15
|
+
context?: RequestContext;
|
|
16
|
+
error?: {
|
|
17
|
+
message: string;
|
|
18
|
+
stack?: string;
|
|
19
|
+
};
|
|
20
|
+
[key: string]: any;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Type for the `data` parameter of the `McpNotificationSender` function.
|
|
24
|
+
*/
|
|
25
|
+
export type McpNotificationData = McpLogPayload | Record<string, unknown>;
|
|
26
|
+
/**
|
|
27
|
+
* Defines the signature for a function that can send MCP log notifications.
|
|
28
|
+
* This function is typically provided by the MCP server instance.
|
|
29
|
+
* @param level - The severity level of the log message.
|
|
30
|
+
* @param data - The payload of the log notification.
|
|
31
|
+
* @param loggerName - An optional name or identifier for the logger/server.
|
|
32
|
+
*/
|
|
33
|
+
export type McpNotificationSender = (level: McpLogLevel, data: McpNotificationData, loggerName?: string) => void;
|
|
34
|
+
/**
|
|
35
|
+
* Singleton Logger class that wraps Winston for robust logging.
|
|
36
|
+
* Supports file logging, conditional console logging, and MCP notifications.
|
|
37
|
+
*/
|
|
38
|
+
export declare class Logger {
|
|
39
|
+
private static instance;
|
|
40
|
+
private winstonLogger?;
|
|
41
|
+
private initialized;
|
|
42
|
+
private mcpNotificationSender?;
|
|
43
|
+
private currentMcpLevel;
|
|
44
|
+
private currentWinstonLevel;
|
|
45
|
+
private readonly MCP_NOTIFICATION_STACK_TRACE_MAX_LENGTH;
|
|
46
|
+
private readonly LOG_FILE_MAX_SIZE;
|
|
47
|
+
private readonly LOG_MAX_FILES;
|
|
48
|
+
/** @private */
|
|
49
|
+
private constructor();
|
|
50
|
+
/**
|
|
51
|
+
* Initializes the Winston logger instance.
|
|
52
|
+
* Should be called once at application startup.
|
|
53
|
+
* @param level - The initial minimum MCP log level.
|
|
54
|
+
*/
|
|
55
|
+
initialize(level?: McpLogLevel): Promise<void>;
|
|
56
|
+
/**
|
|
57
|
+
* Sets the function used to send MCP 'notifications/message'.
|
|
58
|
+
* @param sender - The function to call for sending notifications, or undefined to disable.
|
|
59
|
+
*/
|
|
60
|
+
setMcpNotificationSender(sender: McpNotificationSender | undefined): void;
|
|
61
|
+
/**
|
|
62
|
+
* Dynamically sets the minimum logging level.
|
|
63
|
+
* @param newLevel - The new minimum MCP log level to set.
|
|
64
|
+
*/
|
|
65
|
+
setLevel(newLevel: McpLogLevel): void;
|
|
66
|
+
/**
|
|
67
|
+
* Configures the console transport based on the current log level and TTY status.
|
|
68
|
+
* Adds or removes the console transport as needed.
|
|
69
|
+
* @returns {{ enabled: boolean, message: string | null }} Status of console logging.
|
|
70
|
+
* @private
|
|
71
|
+
*/
|
|
72
|
+
private _configureConsoleTransport;
|
|
73
|
+
/**
|
|
74
|
+
* Gets the singleton instance of the Logger.
|
|
75
|
+
* @returns The singleton Logger instance.
|
|
76
|
+
*/
|
|
77
|
+
static getInstance(): Logger;
|
|
78
|
+
/**
|
|
79
|
+
* Ensures the logger has been initialized.
|
|
80
|
+
* @returns True if initialized, false otherwise.
|
|
81
|
+
* @private
|
|
82
|
+
*/
|
|
83
|
+
private ensureInitialized;
|
|
84
|
+
/**
|
|
85
|
+
* Centralized log processing method.
|
|
86
|
+
* @param level - The MCP severity level of the message.
|
|
87
|
+
* @param msg - The main log message.
|
|
88
|
+
* @param context - Optional request context for the log.
|
|
89
|
+
* @param error - Optional error object associated with the log.
|
|
90
|
+
* @private
|
|
91
|
+
*/
|
|
92
|
+
private log;
|
|
93
|
+
/** Logs a message at the 'debug' level. */
|
|
94
|
+
debug(msg: string, context?: RequestContext): void;
|
|
95
|
+
/** Logs a message at the 'info' level. */
|
|
96
|
+
info(msg: string, context?: RequestContext): void;
|
|
97
|
+
/** Logs a message at the 'notice' level. */
|
|
98
|
+
notice(msg: string, context?: RequestContext): void;
|
|
99
|
+
/** Logs a message at the 'warning' level. */
|
|
100
|
+
warning(msg: string, context?: RequestContext): void;
|
|
101
|
+
/**
|
|
102
|
+
* Logs a message at the 'error' level.
|
|
103
|
+
* @param msg - The main log message.
|
|
104
|
+
* @param err - Optional. Error object or RequestContext.
|
|
105
|
+
* @param context - Optional. RequestContext if `err` is an Error.
|
|
106
|
+
*/
|
|
107
|
+
error(msg: string, err?: Error | RequestContext, context?: RequestContext): void;
|
|
108
|
+
/**
|
|
109
|
+
* Logs a message at the 'crit' (critical) level.
|
|
110
|
+
* @param msg - The main log message.
|
|
111
|
+
* @param err - Optional. Error object or RequestContext.
|
|
112
|
+
* @param context - Optional. RequestContext if `err` is an Error.
|
|
113
|
+
*/
|
|
114
|
+
crit(msg: string, err?: Error | RequestContext, context?: RequestContext): void;
|
|
115
|
+
/**
|
|
116
|
+
* Logs a message at the 'alert' level.
|
|
117
|
+
* @param msg - The main log message.
|
|
118
|
+
* @param err - Optional. Error object or RequestContext.
|
|
119
|
+
* @param context - Optional. RequestContext if `err` is an Error.
|
|
120
|
+
*/
|
|
121
|
+
alert(msg: string, err?: Error | RequestContext, context?: RequestContext): void;
|
|
122
|
+
/**
|
|
123
|
+
* Logs a message at the 'emerg' (emergency) level.
|
|
124
|
+
* @param msg - The main log message.
|
|
125
|
+
* @param err - Optional. Error object or RequestContext.
|
|
126
|
+
* @param context - Optional. RequestContext if `err` is an Error.
|
|
127
|
+
*/
|
|
128
|
+
emerg(msg: string, err?: Error | RequestContext, context?: RequestContext): void;
|
|
129
|
+
/**
|
|
130
|
+
* Logs a message at the 'emerg' (emergency) level, typically for fatal errors.
|
|
131
|
+
* @param msg - The main log message.
|
|
132
|
+
* @param err - Optional. Error object or RequestContext.
|
|
133
|
+
* @param context - Optional. RequestContext if `err` is an Error.
|
|
134
|
+
*/
|
|
135
|
+
fatal(msg: string, err?: Error | RequestContext, context?: RequestContext): void;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* The singleton instance of the Logger.
|
|
139
|
+
* Use this instance for all logging operations.
|
|
140
|
+
*/
|
|
141
|
+
export declare const logger: Logger;
|