@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.
Files changed (121) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +174 -0
  3. package/dist/config/index.d.ts +91 -0
  4. package/dist/config/index.js +324 -0
  5. package/dist/index.d.ts +24 -0
  6. package/dist/index.js +190 -0
  7. package/dist/mcp-server/resources/echoResource/echoResourceLogic.d.ts +79 -0
  8. package/dist/mcp-server/resources/echoResource/echoResourceLogic.js +82 -0
  9. package/dist/mcp-server/resources/echoResource/index.d.ts +13 -0
  10. package/dist/mcp-server/resources/echoResource/index.js +13 -0
  11. package/dist/mcp-server/resources/echoResource/registration.d.ts +30 -0
  12. package/dist/mcp-server/resources/echoResource/registration.js +168 -0
  13. package/dist/mcp-server/server.d.ts +28 -0
  14. package/dist/mcp-server/server.js +159 -0
  15. package/dist/mcp-server/tools/fetchPubMedContent/index.d.ts +6 -0
  16. package/dist/mcp-server/tools/fetchPubMedContent/index.js +6 -0
  17. package/dist/mcp-server/tools/fetchPubMedContent/logic.d.ts +63 -0
  18. package/dist/mcp-server/tools/fetchPubMedContent/logic.js +518 -0
  19. package/dist/mcp-server/tools/fetchPubMedContent/registration.d.ts +10 -0
  20. package/dist/mcp-server/tools/fetchPubMedContent/registration.js +37 -0
  21. package/dist/mcp-server/tools/getPubMedArticleConnections/index.d.ts +6 -0
  22. package/dist/mcp-server/tools/getPubMedArticleConnections/index.js +6 -0
  23. package/dist/mcp-server/tools/getPubMedArticleConnections/logic/citationFormatter.d.ts +9 -0
  24. package/dist/mcp-server/tools/getPubMedArticleConnections/logic/citationFormatter.js +288 -0
  25. package/dist/mcp-server/tools/getPubMedArticleConnections/logic/elinkHandler.d.ts +9 -0
  26. package/dist/mcp-server/tools/getPubMedArticleConnections/logic/elinkHandler.js +208 -0
  27. package/dist/mcp-server/tools/getPubMedArticleConnections/logic/index.d.ts +15 -0
  28. package/dist/mcp-server/tools/getPubMedArticleConnections/logic/index.js +82 -0
  29. package/dist/mcp-server/tools/getPubMedArticleConnections/logic/types.d.ts +27 -0
  30. package/dist/mcp-server/tools/getPubMedArticleConnections/logic/types.js +5 -0
  31. package/dist/mcp-server/tools/getPubMedArticleConnections/logic.d.ts +6 -0
  32. package/dist/mcp-server/tools/getPubMedArticleConnections/logic.js +6 -0
  33. package/dist/mcp-server/tools/getPubMedArticleConnections/registration.d.ts +36 -0
  34. package/dist/mcp-server/tools/getPubMedArticleConnections/registration.js +76 -0
  35. package/dist/mcp-server/tools/pubmedResearchAgent/index.d.ts +6 -0
  36. package/dist/mcp-server/tools/pubmedResearchAgent/index.js +6 -0
  37. package/dist/mcp-server/tools/pubmedResearchAgent/logic/index.d.ts +7 -0
  38. package/dist/mcp-server/tools/pubmedResearchAgent/logic/index.js +9 -0
  39. package/dist/mcp-server/tools/pubmedResearchAgent/logic/inputSchema.d.ts +111 -0
  40. package/dist/mcp-server/tools/pubmedResearchAgent/logic/inputSchema.js +149 -0
  41. package/dist/mcp-server/tools/pubmedResearchAgent/logic/outputTypes.d.ts +106 -0
  42. package/dist/mcp-server/tools/pubmedResearchAgent/logic/outputTypes.js +7 -0
  43. package/dist/mcp-server/tools/pubmedResearchAgent/logic/planOrchestrator.d.ts +10 -0
  44. package/dist/mcp-server/tools/pubmedResearchAgent/logic/planOrchestrator.js +251 -0
  45. package/dist/mcp-server/tools/pubmedResearchAgent/logic.d.ts +12 -0
  46. package/dist/mcp-server/tools/pubmedResearchAgent/logic.js +55 -0
  47. package/dist/mcp-server/tools/pubmedResearchAgent/registration.d.ts +10 -0
  48. package/dist/mcp-server/tools/pubmedResearchAgent/registration.js +36 -0
  49. package/dist/mcp-server/tools/searchPubMedArticles/index.d.ts +6 -0
  50. package/dist/mcp-server/tools/searchPubMedArticles/index.js +6 -0
  51. package/dist/mcp-server/tools/searchPubMedArticles/logic.d.ts +61 -0
  52. package/dist/mcp-server/tools/searchPubMedArticles/logic.js +229 -0
  53. package/dist/mcp-server/tools/searchPubMedArticles/registration.d.ts +10 -0
  54. package/dist/mcp-server/tools/searchPubMedArticles/registration.js +38 -0
  55. package/dist/mcp-server/transports/authentication/authMiddleware.d.ts +29 -0
  56. package/dist/mcp-server/transports/authentication/authMiddleware.js +174 -0
  57. package/dist/mcp-server/transports/httpTransport.d.ts +23 -0
  58. package/dist/mcp-server/transports/httpTransport.js +463 -0
  59. package/dist/mcp-server/transports/stdioTransport.d.ts +42 -0
  60. package/dist/mcp-server/transports/stdioTransport.js +63 -0
  61. package/dist/services/NCBI/ncbiConstants.d.ts +22 -0
  62. package/dist/services/NCBI/ncbiConstants.js +5 -0
  63. package/dist/services/NCBI/ncbiCoreApiClient.d.ts +24 -0
  64. package/dist/services/NCBI/ncbiCoreApiClient.js +118 -0
  65. package/dist/services/NCBI/ncbiRequestQueueManager.d.ts +36 -0
  66. package/dist/services/NCBI/ncbiRequestQueueManager.js +96 -0
  67. package/dist/services/NCBI/ncbiResponseHandler.d.ts +22 -0
  68. package/dist/services/NCBI/ncbiResponseHandler.js +192 -0
  69. package/dist/services/NCBI/ncbiService.d.ts +24 -0
  70. package/dist/services/NCBI/ncbiService.js +57 -0
  71. package/dist/services/index.d.ts +7 -0
  72. package/dist/services/index.js +7 -0
  73. package/dist/services/llm-providers/index.d.ts +7 -0
  74. package/dist/services/llm-providers/index.js +7 -0
  75. package/dist/services/llm-providers/llmFactory.d.ts +69 -0
  76. package/dist/services/llm-providers/llmFactory.js +132 -0
  77. package/dist/services/llm-providers/openRouter/index.d.ts +6 -0
  78. package/dist/services/llm-providers/openRouter/index.js +7 -0
  79. package/dist/services/llm-providers/openRouter/openRouterProvider.d.ts +99 -0
  80. package/dist/services/llm-providers/openRouter/openRouterProvider.js +329 -0
  81. package/dist/types-global/errors.d.ts +119 -0
  82. package/dist/types-global/errors.js +119 -0
  83. package/dist/types-global/pubmedXml.d.ts +350 -0
  84. package/dist/types-global/pubmedXml.js +7 -0
  85. package/dist/utils/index.d.ts +10 -0
  86. package/dist/utils/index.js +18 -0
  87. package/dist/utils/internal/errorHandler.d.ts +176 -0
  88. package/dist/utils/internal/errorHandler.js +335 -0
  89. package/dist/utils/internal/index.d.ts +9 -0
  90. package/dist/utils/internal/index.js +9 -0
  91. package/dist/utils/internal/logger.d.ts +141 -0
  92. package/dist/utils/internal/logger.js +426 -0
  93. package/dist/utils/internal/requestContext.d.ts +83 -0
  94. package/dist/utils/internal/requestContext.js +72 -0
  95. package/dist/utils/metrics/index.d.ts +7 -0
  96. package/dist/utils/metrics/index.js +7 -0
  97. package/dist/utils/metrics/tokenCounter.d.ts +35 -0
  98. package/dist/utils/metrics/tokenCounter.js +110 -0
  99. package/dist/utils/parsing/dateParser.d.ts +73 -0
  100. package/dist/utils/parsing/dateParser.js +107 -0
  101. package/dist/utils/parsing/index.d.ts +8 -0
  102. package/dist/utils/parsing/index.js +8 -0
  103. package/dist/utils/parsing/jsonParser.d.ts +82 -0
  104. package/dist/utils/parsing/jsonParser.js +126 -0
  105. package/dist/utils/parsing/ncbi-parsing/eSummaryResultParser.d.ts +32 -0
  106. package/dist/utils/parsing/ncbi-parsing/eSummaryResultParser.js +295 -0
  107. package/dist/utils/parsing/ncbi-parsing/index.d.ts +8 -0
  108. package/dist/utils/parsing/ncbi-parsing/index.js +8 -0
  109. package/dist/utils/parsing/ncbi-parsing/pubmedArticleStructureParser.d.ts +70 -0
  110. package/dist/utils/parsing/ncbi-parsing/pubmedArticleStructureParser.js +229 -0
  111. package/dist/utils/parsing/ncbi-parsing/xmlGenericHelpers.d.ts +31 -0
  112. package/dist/utils/parsing/ncbi-parsing/xmlGenericHelpers.js +76 -0
  113. package/dist/utils/security/idGenerator.d.ts +134 -0
  114. package/dist/utils/security/idGenerator.js +198 -0
  115. package/dist/utils/security/index.d.ts +9 -0
  116. package/dist/utils/security/index.js +9 -0
  117. package/dist/utils/security/rateLimiter.d.ts +108 -0
  118. package/dist/utils/security/rateLimiter.js +179 -0
  119. package/dist/utils/security/sanitization.d.ts +173 -0
  120. package/dist/utils/security/sanitization.js +439 -0
  121. package/package.json +97 -0
@@ -0,0 +1,76 @@
1
+ /**
2
+ * @fileoverview Generic helper functions for parsing XML data, particularly
3
+ * structures from fast-xml-parser.
4
+ * @module src/utils/parsing/ncbi-parsing/xmlGenericHelpers
5
+ */
6
+ /**
7
+ * Ensures that the input is an array. If it's not an array, it wraps it in one.
8
+ * Handles undefined or null by returning an empty array.
9
+ * @param item - The item to ensure is an array.
10
+ * @returns An array containing the item, or an empty array if item is null/undefined.
11
+ * @template T - The type of the items in the array.
12
+ */
13
+ export function ensureArray(item) {
14
+ if (item === undefined || item === null) {
15
+ return [];
16
+ }
17
+ return Array.isArray(item) ? item : [item];
18
+ }
19
+ /**
20
+ * Safely extracts text content from an XML element, which might be a string or an object with a "#text" property.
21
+ * Handles cases where #text might be a number or boolean by converting to string.
22
+ * @param element - The XML element (string, object with #text, or undefined).
23
+ * @param defaultValue - The value to return if text cannot be extracted. Defaults to an empty string.
24
+ * @returns The text content or the default value.
25
+ */
26
+ export function getText(element, defaultValue = "") {
27
+ if (element === undefined || element === null) {
28
+ return defaultValue;
29
+ }
30
+ if (typeof element === "string") {
31
+ return element;
32
+ }
33
+ if (typeof element === "number" || typeof element === "boolean") {
34
+ return String(element); // Handle direct number/boolean elements
35
+ }
36
+ if (typeof element === "object" && element["#text"] !== undefined) {
37
+ // Check if #text exists and convert to string
38
+ if (typeof element["#text"] === "string") {
39
+ return element["#text"];
40
+ }
41
+ // Also handle #text being a number or boolean
42
+ if (typeof element["#text"] === "number" ||
43
+ typeof element["#text"] === "boolean") {
44
+ return String(element["#text"]);
45
+ }
46
+ }
47
+ return defaultValue;
48
+ }
49
+ /**
50
+ * Safely extracts an attribute value from an XML element.
51
+ * Assumes attributes are prefixed with "@_" by fast-xml-parser.
52
+ * @param element - The XML element object.
53
+ * @param attributeName - The name of the attribute (e.g., "_UI", "_MajorTopicYN", without the "@_" prefix).
54
+ * @param defaultValue - The value to return if the attribute is not found. Defaults to an empty string.
55
+ * @returns The attribute value or the default value.
56
+ */
57
+ export function getAttribute(element, attributeName, // e.g., "UI", "MajorTopicYN"
58
+ defaultValue = "") {
59
+ const fullAttributeName = `@_${attributeName}`; // As per fast-xml-parser config
60
+ if (element &&
61
+ typeof element === "object" &&
62
+ typeof element[fullAttributeName] === "string") {
63
+ return element[fullAttributeName];
64
+ }
65
+ if (element &&
66
+ typeof element === "object" &&
67
+ typeof element[fullAttributeName] === "boolean") {
68
+ return String(element[fullAttributeName]); // Convert boolean attributes to string
69
+ }
70
+ if (element &&
71
+ typeof element === "object" &&
72
+ typeof element[fullAttributeName] === "number") {
73
+ return String(element[fullAttributeName]); // Convert number attributes to string
74
+ }
75
+ return defaultValue;
76
+ }
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Defines the structure for configuring entity prefixes.
3
+ * Keys are entity type names (e.g., "project", "task"), and values are their corresponding ID prefixes (e.g., "PROJ", "TASK").
4
+ */
5
+ export interface EntityPrefixConfig {
6
+ [key: string]: string;
7
+ }
8
+ /**
9
+ * Defines options for customizing ID generation.
10
+ */
11
+ export interface IdGenerationOptions {
12
+ length?: number;
13
+ separator?: string;
14
+ charset?: string;
15
+ }
16
+ /**
17
+ * A generic ID Generator class for creating and managing unique, prefixed identifiers.
18
+ * Allows defining custom prefixes, generating random strings, and validating/normalizing IDs.
19
+ */
20
+ export declare class IdGenerator {
21
+ /**
22
+ * Default character set for the random part of the ID.
23
+ * @private
24
+ */
25
+ private static DEFAULT_CHARSET;
26
+ /**
27
+ * Default separator character between prefix and random part.
28
+ * @private
29
+ */
30
+ private static DEFAULT_SEPARATOR;
31
+ /**
32
+ * Default length for the random part of the ID.
33
+ * @private
34
+ */
35
+ private static DEFAULT_LENGTH;
36
+ /**
37
+ * Stores the mapping of entity types to their prefixes.
38
+ * @private
39
+ */
40
+ private entityPrefixes;
41
+ /**
42
+ * Stores a reverse mapping from prefixes (case-insensitive) to entity types.
43
+ * @private
44
+ */
45
+ private prefixToEntityType;
46
+ /**
47
+ * Constructs an `IdGenerator` instance.
48
+ * @param entityPrefixes - An initial map of entity types to their prefixes.
49
+ */
50
+ constructor(entityPrefixes?: EntityPrefixConfig);
51
+ /**
52
+ * Sets or updates the entity prefix configuration and rebuilds the internal reverse lookup map.
53
+ * @param entityPrefixes - A map where keys are entity type names and values are their desired ID prefixes.
54
+ */
55
+ setEntityPrefixes(entityPrefixes: EntityPrefixConfig): void;
56
+ /**
57
+ * Retrieves a copy of the current entity prefix configuration.
58
+ * @returns The current entity prefix configuration.
59
+ */
60
+ getEntityPrefixes(): EntityPrefixConfig;
61
+ /**
62
+ * Generates a cryptographically secure random string.
63
+ * @param length - The desired length of the random string. Defaults to `IdGenerator.DEFAULT_LENGTH`.
64
+ * @param charset - The character set to use. Defaults to `IdGenerator.DEFAULT_CHARSET`.
65
+ * @returns The generated random string.
66
+ */
67
+ generateRandomString(length?: number, charset?: string): string;
68
+ /**
69
+ * Generates a unique ID, optionally prepended with a prefix.
70
+ * @param prefix - An optional prefix for the ID.
71
+ * @param options - Optional parameters for ID generation (length, separator, charset).
72
+ * @returns A unique identifier string.
73
+ */
74
+ generate(prefix?: string, options?: IdGenerationOptions): string;
75
+ /**
76
+ * Generates a unique ID for a specified entity type, using its configured prefix.
77
+ * @param entityType - The type of entity (must be registered).
78
+ * @param options - Optional parameters for ID generation.
79
+ * @returns A unique identifier string for the entity (e.g., "PROJ_A6B3J0").
80
+ * @throws {McpError} If the `entityType` is not registered.
81
+ */
82
+ generateForEntity(entityType: string, options?: IdGenerationOptions): string;
83
+ /**
84
+ * Validates if an ID conforms to the expected format for a specific entity type.
85
+ * @param id - The ID string to validate.
86
+ * @param entityType - The expected entity type of the ID.
87
+ * @param options - Optional parameters used during generation for validation consistency.
88
+ * @returns `true` if the ID is valid, `false` otherwise.
89
+ */
90
+ isValid(id: string, entityType: string, options?: IdGenerationOptions): boolean;
91
+ /**
92
+ * Escapes special characters in a string for use in a regular expression.
93
+ * @param str - The string to escape.
94
+ * @returns The escaped string.
95
+ * @private
96
+ */
97
+ private escapeRegex;
98
+ /**
99
+ * Strips the prefix and separator from an ID string.
100
+ * @param id - The ID string (e.g., "PROJ_A6B3J0").
101
+ * @param separator - The separator used in the ID. Defaults to `IdGenerator.DEFAULT_SEPARATOR`.
102
+ * @returns The ID part without the prefix, or the original ID if separator not found.
103
+ */
104
+ stripPrefix(id: string, separator?: string): string;
105
+ /**
106
+ * Determines the entity type from an ID string by its prefix (case-insensitive).
107
+ * @param id - The ID string (e.g., "PROJ_A6B3J0").
108
+ * @param separator - The separator used in the ID. Defaults to `IdGenerator.DEFAULT_SEPARATOR`.
109
+ * @returns The determined entity type.
110
+ * @throws {McpError} If ID format is invalid or prefix is unknown.
111
+ */
112
+ getEntityType(id: string, separator?: string): string;
113
+ /**
114
+ * Normalizes an entity ID to ensure the prefix matches the registered case
115
+ * and the random part is uppercase.
116
+ * @param id - The ID to normalize (e.g., "proj_a6b3j0").
117
+ * @param separator - The separator used in the ID. Defaults to `IdGenerator.DEFAULT_SEPARATOR`.
118
+ * @returns The normalized ID (e.g., "PROJ_A6B3J0").
119
+ * @throws {McpError} If the entity type cannot be determined from the ID.
120
+ */
121
+ normalize(id: string, separator?: string): string;
122
+ }
123
+ /**
124
+ * Default singleton instance of the `IdGenerator`.
125
+ * Initialize with `idGenerator.setEntityPrefixes({})` to configure.
126
+ */
127
+ export declare const idGenerator: IdGenerator;
128
+ /**
129
+ * Generates a standard Version 4 UUID (Universally Unique Identifier).
130
+ * Uses the Node.js `crypto` module. This function is independent of the IdGenerator instance
131
+ * to prevent circular dependencies when used by other utilities like requestContextService.
132
+ * @returns A new UUID string.
133
+ */
134
+ export declare const generateUUID: () => string;
@@ -0,0 +1,198 @@
1
+ /**
2
+ * @fileoverview Provides a utility class `IdGenerator` for creating customizable, prefixed unique identifiers,
3
+ * and a standalone `generateUUID` function for generating standard UUIDs.
4
+ * The `IdGenerator` supports entity-specific prefixes, custom character sets, and lengths.
5
+ *
6
+ * Note: Logging has been removed from this module to prevent circular dependencies
7
+ * with the `requestContextService`, which itself uses `generateUUID` from this module.
8
+ * This was causing `ReferenceError: Cannot access 'generateUUID' before initialization`
9
+ * during application startup.
10
+ * @module src/utils/security/idGenerator
11
+ */
12
+ import { randomUUID as cryptoRandomUUID, randomBytes } from "crypto";
13
+ import { BaseErrorCode, McpError } from "../../types-global/errors.js";
14
+ /**
15
+ * A generic ID Generator class for creating and managing unique, prefixed identifiers.
16
+ * Allows defining custom prefixes, generating random strings, and validating/normalizing IDs.
17
+ */
18
+ export class IdGenerator {
19
+ /**
20
+ * Constructs an `IdGenerator` instance.
21
+ * @param entityPrefixes - An initial map of entity types to their prefixes.
22
+ */
23
+ constructor(entityPrefixes = {}) {
24
+ /**
25
+ * Stores the mapping of entity types to their prefixes.
26
+ * @private
27
+ */
28
+ this.entityPrefixes = {};
29
+ /**
30
+ * Stores a reverse mapping from prefixes (case-insensitive) to entity types.
31
+ * @private
32
+ */
33
+ this.prefixToEntityType = {};
34
+ // Logging removed to prevent circular dependency with requestContextService.
35
+ this.setEntityPrefixes(entityPrefixes);
36
+ }
37
+ /**
38
+ * Sets or updates the entity prefix configuration and rebuilds the internal reverse lookup map.
39
+ * @param entityPrefixes - A map where keys are entity type names and values are their desired ID prefixes.
40
+ */
41
+ setEntityPrefixes(entityPrefixes) {
42
+ // Logging removed.
43
+ this.entityPrefixes = { ...entityPrefixes };
44
+ this.prefixToEntityType = Object.entries(this.entityPrefixes).reduce((acc, [type, prefix]) => {
45
+ acc[prefix.toLowerCase()] = type; // Store lowercase for case-insensitive lookup
46
+ return acc;
47
+ }, {});
48
+ }
49
+ /**
50
+ * Retrieves a copy of the current entity prefix configuration.
51
+ * @returns The current entity prefix configuration.
52
+ */
53
+ getEntityPrefixes() {
54
+ return { ...this.entityPrefixes };
55
+ }
56
+ /**
57
+ * Generates a cryptographically secure random string.
58
+ * @param length - The desired length of the random string. Defaults to `IdGenerator.DEFAULT_LENGTH`.
59
+ * @param charset - The character set to use. Defaults to `IdGenerator.DEFAULT_CHARSET`.
60
+ * @returns The generated random string.
61
+ */
62
+ generateRandomString(length = IdGenerator.DEFAULT_LENGTH, charset = IdGenerator.DEFAULT_CHARSET) {
63
+ const bytes = randomBytes(length);
64
+ let result = "";
65
+ for (let i = 0; i < length; i++) {
66
+ result += charset[bytes[i] % charset.length];
67
+ }
68
+ return result;
69
+ }
70
+ /**
71
+ * Generates a unique ID, optionally prepended with a prefix.
72
+ * @param prefix - An optional prefix for the ID.
73
+ * @param options - Optional parameters for ID generation (length, separator, charset).
74
+ * @returns A unique identifier string.
75
+ */
76
+ generate(prefix, options = {}) {
77
+ // Logging removed.
78
+ const { length = IdGenerator.DEFAULT_LENGTH, separator = IdGenerator.DEFAULT_SEPARATOR, charset = IdGenerator.DEFAULT_CHARSET, } = options;
79
+ const randomPart = this.generateRandomString(length, charset);
80
+ const generatedId = prefix
81
+ ? `${prefix}${separator}${randomPart}`
82
+ : randomPart;
83
+ return generatedId;
84
+ }
85
+ /**
86
+ * Generates a unique ID for a specified entity type, using its configured prefix.
87
+ * @param entityType - The type of entity (must be registered).
88
+ * @param options - Optional parameters for ID generation.
89
+ * @returns A unique identifier string for the entity (e.g., "PROJ_A6B3J0").
90
+ * @throws {McpError} If the `entityType` is not registered.
91
+ */
92
+ generateForEntity(entityType, options = {}) {
93
+ const prefix = this.entityPrefixes[entityType];
94
+ if (!prefix) {
95
+ throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Unknown entity type: ${entityType}. No prefix registered.`);
96
+ }
97
+ return this.generate(prefix, options);
98
+ }
99
+ /**
100
+ * Validates if an ID conforms to the expected format for a specific entity type.
101
+ * @param id - The ID string to validate.
102
+ * @param entityType - The expected entity type of the ID.
103
+ * @param options - Optional parameters used during generation for validation consistency.
104
+ * @returns `true` if the ID is valid, `false` otherwise.
105
+ */
106
+ isValid(id, entityType, options = {}) {
107
+ const prefix = this.entityPrefixes[entityType];
108
+ const { length = IdGenerator.DEFAULT_LENGTH, separator = IdGenerator.DEFAULT_SEPARATOR, } = options;
109
+ if (!prefix) {
110
+ return false;
111
+ }
112
+ // Assumes default charset characters (uppercase letters and digits) for regex.
113
+ const pattern = new RegExp(`^${this.escapeRegex(prefix)}${this.escapeRegex(separator)}[A-Z0-9]{${length}}$`);
114
+ return pattern.test(id);
115
+ }
116
+ /**
117
+ * Escapes special characters in a string for use in a regular expression.
118
+ * @param str - The string to escape.
119
+ * @returns The escaped string.
120
+ * @private
121
+ */
122
+ escapeRegex(str) {
123
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
124
+ }
125
+ /**
126
+ * Strips the prefix and separator from an ID string.
127
+ * @param id - The ID string (e.g., "PROJ_A6B3J0").
128
+ * @param separator - The separator used in the ID. Defaults to `IdGenerator.DEFAULT_SEPARATOR`.
129
+ * @returns The ID part without the prefix, or the original ID if separator not found.
130
+ */
131
+ stripPrefix(id, separator = IdGenerator.DEFAULT_SEPARATOR) {
132
+ const parts = id.split(separator);
133
+ return parts.length > 1 ? parts.slice(1).join(separator) : id; // Handle separators in random part
134
+ }
135
+ /**
136
+ * Determines the entity type from an ID string by its prefix (case-insensitive).
137
+ * @param id - The ID string (e.g., "PROJ_A6B3J0").
138
+ * @param separator - The separator used in the ID. Defaults to `IdGenerator.DEFAULT_SEPARATOR`.
139
+ * @returns The determined entity type.
140
+ * @throws {McpError} If ID format is invalid or prefix is unknown.
141
+ */
142
+ getEntityType(id, separator = IdGenerator.DEFAULT_SEPARATOR) {
143
+ const parts = id.split(separator);
144
+ if (parts.length < 2 || !parts[0]) {
145
+ throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Invalid ID format: ${id}. Expected format like: PREFIX${separator}RANDOMLPART`);
146
+ }
147
+ const prefix = parts[0];
148
+ const entityType = this.prefixToEntityType[prefix.toLowerCase()];
149
+ if (!entityType) {
150
+ throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Unknown entity type for prefix: ${prefix}`);
151
+ }
152
+ return entityType;
153
+ }
154
+ /**
155
+ * Normalizes an entity ID to ensure the prefix matches the registered case
156
+ * and the random part is uppercase.
157
+ * @param id - The ID to normalize (e.g., "proj_a6b3j0").
158
+ * @param separator - The separator used in the ID. Defaults to `IdGenerator.DEFAULT_SEPARATOR`.
159
+ * @returns The normalized ID (e.g., "PROJ_A6B3J0").
160
+ * @throws {McpError} If the entity type cannot be determined from the ID.
161
+ */
162
+ normalize(id, separator = IdGenerator.DEFAULT_SEPARATOR) {
163
+ const entityType = this.getEntityType(id, separator);
164
+ const registeredPrefix = this.entityPrefixes[entityType];
165
+ const idParts = id.split(separator);
166
+ const randomPart = idParts.slice(1).join(separator);
167
+ return `${registeredPrefix}${separator}${randomPart.toUpperCase()}`;
168
+ }
169
+ }
170
+ /**
171
+ * Default character set for the random part of the ID.
172
+ * @private
173
+ */
174
+ IdGenerator.DEFAULT_CHARSET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
175
+ /**
176
+ * Default separator character between prefix and random part.
177
+ * @private
178
+ */
179
+ IdGenerator.DEFAULT_SEPARATOR = "_";
180
+ /**
181
+ * Default length for the random part of the ID.
182
+ * @private
183
+ */
184
+ IdGenerator.DEFAULT_LENGTH = 6;
185
+ /**
186
+ * Default singleton instance of the `IdGenerator`.
187
+ * Initialize with `idGenerator.setEntityPrefixes({})` to configure.
188
+ */
189
+ export const idGenerator = new IdGenerator();
190
+ /**
191
+ * Generates a standard Version 4 UUID (Universally Unique Identifier).
192
+ * Uses the Node.js `crypto` module. This function is independent of the IdGenerator instance
193
+ * to prevent circular dependencies when used by other utilities like requestContextService.
194
+ * @returns A new UUID string.
195
+ */
196
+ export const generateUUID = () => {
197
+ return cryptoRandomUUID();
198
+ };
@@ -0,0 +1,9 @@
1
+ /**
2
+ * @fileoverview Barrel file for security-related utility modules.
3
+ * This file re-exports utilities for input sanitization, rate limiting,
4
+ * and ID generation.
5
+ * @module src/utils/security
6
+ */
7
+ export * from "./idGenerator.js";
8
+ export * from "./rateLimiter.js";
9
+ export * from "./sanitization.js";
@@ -0,0 +1,9 @@
1
+ /**
2
+ * @fileoverview Barrel file for security-related utility modules.
3
+ * This file re-exports utilities for input sanitization, rate limiting,
4
+ * and ID generation.
5
+ * @module src/utils/security
6
+ */
7
+ export * from "./idGenerator.js";
8
+ export * from "./rateLimiter.js";
9
+ export * from "./sanitization.js";
@@ -0,0 +1,108 @@
1
+ import { RequestContext } from "../index.js";
2
+ /**
3
+ * Defines configuration options for the {@link RateLimiter}.
4
+ */
5
+ export interface RateLimitConfig {
6
+ /** Time window in milliseconds. */
7
+ windowMs: number;
8
+ /** Maximum number of requests allowed in the window. */
9
+ maxRequests: number;
10
+ /** Custom error message template. Can include `{waitTime}` placeholder. */
11
+ errorMessage?: string;
12
+ /** If true, skip rate limiting in development. */
13
+ skipInDevelopment?: boolean;
14
+ /** Optional function to generate a custom key for rate limiting. */
15
+ keyGenerator?: (identifier: string, context?: RequestContext) => string;
16
+ /** How often, in milliseconds, to clean up expired entries. */
17
+ cleanupInterval?: number;
18
+ }
19
+ /**
20
+ * Represents an individual entry for tracking requests against a rate limit key.
21
+ */
22
+ export interface RateLimitEntry {
23
+ /** Current request count. */
24
+ count: number;
25
+ /** When the window resets (timestamp in milliseconds). */
26
+ resetTime: number;
27
+ }
28
+ /**
29
+ * A generic rate limiter class using an in-memory store.
30
+ * Controls frequency of operations based on unique keys.
31
+ */
32
+ export declare class RateLimiter {
33
+ private config;
34
+ /**
35
+ * Stores current request counts and reset times for each key.
36
+ * @private
37
+ */
38
+ private limits;
39
+ /**
40
+ * Timer ID for periodic cleanup.
41
+ * @private
42
+ */
43
+ private cleanupTimer;
44
+ /**
45
+ * Default configuration values.
46
+ * @private
47
+ */
48
+ private static DEFAULT_CONFIG;
49
+ /**
50
+ * Creates a new `RateLimiter` instance.
51
+ * @param config - Configuration options, merged with defaults.
52
+ */
53
+ constructor(config: RateLimitConfig);
54
+ /**
55
+ * Starts the periodic timer to clean up expired rate limit entries.
56
+ * @private
57
+ */
58
+ private startCleanupTimer;
59
+ /**
60
+ * Removes expired rate limit entries from the store.
61
+ * @private
62
+ */
63
+ private cleanupExpiredEntries;
64
+ /**
65
+ * Updates the configuration of the rate limiter instance.
66
+ * @param config - New configuration options to merge.
67
+ */
68
+ configure(config: Partial<RateLimitConfig>): void;
69
+ /**
70
+ * Retrieves a copy of the current rate limiter configuration.
71
+ * @returns The current configuration.
72
+ */
73
+ getConfig(): RateLimitConfig;
74
+ /**
75
+ * Resets all rate limits by clearing the internal store.
76
+ */
77
+ reset(): void;
78
+ /**
79
+ * Checks if a request exceeds the configured rate limit.
80
+ * Throws an `McpError` if the limit is exceeded.
81
+ *
82
+ * @param key - A unique identifier for the request source.
83
+ * @param context - Optional request context for custom key generation.
84
+ * @throws {McpError} If the rate limit is exceeded.
85
+ */
86
+ check(key: string, context?: RequestContext): void;
87
+ /**
88
+ * Retrieves the current rate limit status for a specific key.
89
+ * @param key - The rate limit key.
90
+ * @returns Status object or `null` if no entry exists.
91
+ */
92
+ getStatus(key: string): {
93
+ current: number;
94
+ limit: number;
95
+ remaining: number;
96
+ resetTime: number;
97
+ } | null;
98
+ /**
99
+ * Stops the cleanup timer and clears all rate limit entries.
100
+ * Call when the rate limiter is no longer needed.
101
+ */
102
+ dispose(): void;
103
+ }
104
+ /**
105
+ * Default singleton instance of the `RateLimiter`.
106
+ * Initialized with default configuration. Use `rateLimiter.configure({})` to customize.
107
+ */
108
+ export declare const rateLimiter: RateLimiter;