mcp-ts-template 1.2.2 → 1.2.3

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 (50) hide show
  1. package/README.md +1 -1
  2. package/dist/config/index.d.ts +1 -1
  3. package/dist/config/index.js +1 -1
  4. package/dist/mcp-client/client.js +2 -2
  5. package/dist/mcp-server/resources/echoResource/echoResourceLogic.d.ts +1 -1
  6. package/dist/mcp-server/resources/echoResource/echoResourceLogic.js +1 -1
  7. package/dist/mcp-server/resources/echoResource/index.d.ts +1 -1
  8. package/dist/mcp-server/resources/echoResource/index.js +1 -1
  9. package/dist/mcp-server/resources/echoResource/registration.d.ts +1 -1
  10. package/dist/mcp-server/resources/echoResource/registration.js +1 -1
  11. package/dist/mcp-server/server.d.ts +1 -1
  12. package/dist/mcp-server/server.js +1 -1
  13. package/dist/mcp-server/tools/echoTool/echoToolLogic.d.ts +1 -1
  14. package/dist/mcp-server/tools/echoTool/echoToolLogic.js +1 -1
  15. package/dist/mcp-server/tools/echoTool/index.d.ts +1 -1
  16. package/dist/mcp-server/tools/echoTool/index.js +1 -1
  17. package/dist/mcp-server/tools/echoTool/registration.d.ts +1 -1
  18. package/dist/mcp-server/tools/echoTool/registration.js +1 -1
  19. package/dist/mcp-server/transports/authentication/authMiddleware.d.ts +1 -1
  20. package/dist/mcp-server/transports/authentication/authMiddleware.js +38 -14
  21. package/dist/mcp-server/transports/httpTransport.d.ts +1 -1
  22. package/dist/mcp-server/transports/httpTransport.js +1 -1
  23. package/dist/mcp-server/transports/stdioTransport.d.ts +1 -1
  24. package/dist/mcp-server/transports/stdioTransport.js +1 -1
  25. package/dist/services/openRouterProvider.js +1 -1
  26. package/dist/types-global/errors.d.ts +1 -1
  27. package/dist/types-global/errors.js +1 -1
  28. package/dist/utils/index.d.ts +2 -2
  29. package/dist/utils/index.js +2 -2
  30. package/dist/utils/internal/errorHandler.d.ts +1 -1
  31. package/dist/utils/internal/errorHandler.js +1 -1
  32. package/dist/utils/internal/index.d.ts +1 -1
  33. package/dist/utils/internal/index.js +1 -1
  34. package/dist/utils/internal/logger.js +3 -2
  35. package/dist/utils/internal/requestContext.d.ts +1 -1
  36. package/dist/utils/internal/requestContext.js +2 -2
  37. package/dist/utils/metrics/index.d.ts +1 -1
  38. package/dist/utils/metrics/index.js +1 -1
  39. package/dist/utils/metrics/tokenCounter.d.ts +1 -1
  40. package/dist/utils/parsing/dateParser.d.ts +1 -1
  41. package/dist/utils/parsing/dateParser.js +1 -1
  42. package/dist/utils/parsing/index.d.ts +2 -2
  43. package/dist/utils/parsing/index.js +2 -2
  44. package/dist/utils/parsing/jsonParser.js +13 -4
  45. package/dist/utils/security/idGenerator.js +5 -3
  46. package/dist/utils/security/index.d.ts +3 -3
  47. package/dist/utils/security/index.js +3 -3
  48. package/dist/utils/security/rateLimiter.js +1 -1
  49. package/dist/utils/security/sanitization.js +82 -19
  50. package/package.json +1 -1
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  [![TypeScript](https://img.shields.io/badge/TypeScript-^5.8.3-blue.svg)](https://www.typescriptlang.org/)
4
4
  [![Model Context Protocol SDK](https://img.shields.io/badge/MCP%20SDK-1.11.4-green.svg)](https://github.com/modelcontextprotocol/typescript-sdk)
5
5
  [![MCP Spec Version](https://img.shields.io/badge/MCP%20Spec-2025--03--26-lightgrey.svg)](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-03-26/changelog.mdx)
6
- [![Version](https://img.shields.io/badge/Version-1.2.2-blue.svg)](./CHANGELOG.md)
6
+ [![Version](https://img.shields.io/badge/Version-1.2.3-blue.svg)](./CHANGELOG.md)
7
7
  [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
8
8
  [![Status](https://img.shields.io/badge/Status-Stable-green.svg)](https://github.com/cyanheads/mcp-ts-template/issues)
9
9
  [![GitHub](https://img.shields.io/github/stars/cyanheads/mcp-ts-template?style=social)](https://github.com/cyanheads/mcp-ts-template)
@@ -12,7 +12,7 @@
12
12
  * - Construct and export a comprehensive `config` object.
13
13
  * - Export individual configuration values like `logLevel` and `environment` for convenience.
14
14
  *
15
- * @module config/index
15
+ * @module src/config/index
16
16
  */
17
17
  /**
18
18
  * Main application configuration object.
@@ -12,7 +12,7 @@
12
12
  * - Construct and export a comprehensive `config` object.
13
13
  * - Export individual configuration values like `logLevel` and `environment` for convenience.
14
14
  *
15
- * @module config/index
15
+ * @module src/config/index
16
16
  */
17
17
  import dotenv from "dotenv";
18
18
  import { readFileSync } from "fs";
@@ -302,8 +302,8 @@ export async function disconnectAllMcpClients(parentContext) {
302
302
  error: result.reason instanceof Error
303
303
  ? result.reason.message
304
304
  : String(result.reason), // Get the error message
305
- stack: // Include stack trace if available
306
- result.reason instanceof Error ? result.reason.stack : undefined,
305
+ // Include stack trace if available
306
+ stack: result.reason instanceof Error ? result.reason.stack : undefined,
307
307
  });
308
308
  }
309
309
  else {
@@ -4,7 +4,7 @@
4
4
  * and the main processing function that constructs the resource response.
5
5
  * The echo resource is designed to return a message, typically extracted from the
6
6
  * request URI's path or query parameters, along with a timestamp.
7
- * @module mcp-server/resources/echoResource/echoResourceLogic
7
+ * @module src/mcp-server/resources/echoResource/echoResourceLogic
8
8
  */
9
9
  import { z } from "zod";
10
10
  import { type RequestContext } from "../../../utils/index.js";
@@ -4,7 +4,7 @@
4
4
  * and the main processing function that constructs the resource response.
5
5
  * The echo resource is designed to return a message, typically extracted from the
6
6
  * request URI's path or query parameters, along with a timestamp.
7
- * @module mcp-server/resources/echoResource/echoResourceLogic
7
+ * @module src/mcp-server/resources/echoResource/echoResourceLogic
8
8
  */
9
9
  import { z } from "zod";
10
10
  import { logger } from "../../../utils/index.js";
@@ -8,6 +8,6 @@
8
8
  *
9
9
  * Consuming modules should import from this barrel file to access
10
10
  * the echo resource's registration capabilities.
11
- * @module mcp-server/resources/echoResource/index
11
+ * @module src/mcp-server/resources/echoResource/index
12
12
  */
13
13
  export { registerEchoResource } from "./registration.js";
@@ -8,6 +8,6 @@
8
8
  *
9
9
  * Consuming modules should import from this barrel file to access
10
10
  * the echo resource's registration capabilities.
11
- * @module mcp-server/resources/echoResource/index
11
+ * @module src/mcp-server/resources/echoResource/index
12
12
  */
13
13
  export { registerEchoResource } from "./registration.js";
@@ -4,7 +4,7 @@
4
4
  * and the asynchronous handler function that processes `resources/read` requests matching the template.
5
5
  * It utilizes the MCP SDK's `server.resource()` method for registration and integrates
6
6
  * robust error handling using the project's `ErrorHandler` utility.
7
- * @module mcp-server/resources/echoResource/registration
7
+ * @module src/mcp-server/resources/echoResource/registration
8
8
  */
9
9
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
10
10
  /**
@@ -4,7 +4,7 @@
4
4
  * and the asynchronous handler function that processes `resources/read` requests matching the template.
5
5
  * It utilizes the MCP SDK's `server.resource()` method for registration and integrates
6
6
  * robust error handling using the project's `ErrorHandler` utility.
7
- * @module mcp-server/resources/echoResource/registration
7
+ * @module src/mcp-server/resources/echoResource/registration
8
8
  */
9
9
  import { ResourceTemplate, } from "@modelcontextprotocol/sdk/server/mcp.js";
10
10
  import { BaseErrorCode, McpError } from "../../../types-global/errors.js";
@@ -11,7 +11,7 @@
11
11
  * - Lifecycle: https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-03-26/basic/lifecycle.mdx
12
12
  * - Overview (Capabilities): https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-03-26/basic/index.mdx
13
13
  * - Transports: https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-03-26/basic/transports.mdx
14
- * @module mcp-server/server
14
+ * @module src/mcp-server/server
15
15
  */
16
16
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
17
17
  /**
@@ -11,7 +11,7 @@
11
11
  * - Lifecycle: https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-03-26/basic/lifecycle.mdx
12
12
  * - Overview (Capabilities): https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-03-26/basic/index.mdx
13
13
  * - Transports: https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-03-26/basic/transports.mdx
14
- * @module mcp-server/server
14
+ * @module src/mcp-server/server
15
15
  */
16
16
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
17
17
  import { config, environment } from "../config/index.js";
@@ -2,7 +2,7 @@
2
2
  * @fileoverview Defines the core logic, schemas, and types for the `echo_message` tool.
3
3
  * This module includes input validation using Zod, type definitions for input and output,
4
4
  * and the main processing function that handles message formatting and repetition.
5
- * @module mcp-server/tools/echoTool/echoToolLogic
5
+ * @module src/mcp-server/tools/echoTool/echoToolLogic
6
6
  */
7
7
  import { z } from "zod";
8
8
  import { type RequestContext } from "../../../utils/index.js";
@@ -2,7 +2,7 @@
2
2
  * @fileoverview Defines the core logic, schemas, and types for the `echo_message` tool.
3
3
  * This module includes input validation using Zod, type definitions for input and output,
4
4
  * and the main processing function that handles message formatting and repetition.
5
- * @module mcp-server/tools/echoTool/echoToolLogic
5
+ * @module src/mcp-server/tools/echoTool/echoToolLogic
6
6
  */
7
7
  import { z } from "zod";
8
8
  import { logger } from "../../../utils/index.js";
@@ -7,6 +7,6 @@
7
7
  *
8
8
  * Consuming modules should import from this barrel file to access
9
9
  * the echo tool's registration capabilities.
10
- * @module mcp-server/tools/echoTool/index
10
+ * @module src/mcp-server/tools/echoTool/index
11
11
  */
12
12
  export { registerEchoTool } from "./registration.js";
@@ -7,6 +7,6 @@
7
7
  *
8
8
  * Consuming modules should import from this barrel file to access
9
9
  * the echo tool's registration capabilities.
10
- * @module mcp-server/tools/echoTool/index
10
+ * @module src/mcp-server/tools/echoTool/index
11
11
  */
12
12
  export { registerEchoTool } from "./registration.js";
@@ -4,7 +4,7 @@
4
4
  * and the asynchronous handler function that processes tool invocation requests.
5
5
  * It leverages the MCP SDK's `server.tool()` method for registration and integrates
6
6
  * robust error handling using the project's `ErrorHandler` utility.
7
- * @module mcp-server/tools/echoTool/registration
7
+ * @module src/mcp-server/tools/echoTool/registration
8
8
  */
9
9
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
10
10
  /**
@@ -4,7 +4,7 @@
4
4
  * and the asynchronous handler function that processes tool invocation requests.
5
5
  * It leverages the MCP SDK's `server.tool()` method for registration and integrates
6
6
  * robust error handling using the project's `ErrorHandler` utility.
7
- * @module mcp-server/tools/echoTool/registration
7
+ * @module src/mcp-server/tools/echoTool/registration
8
8
  */
9
9
  import { BaseErrorCode, McpError } from "../../../types-global/errors.js";
10
10
  import { ErrorHandler, logger, requestContextService, } from "../../../utils/index.js";
@@ -11,7 +11,7 @@
11
11
  * If the token is missing, invalid, or expired, it sends an HTTP 401 Unauthorized response.
12
12
  *
13
13
  * @see {@link https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-03-26/basic/authorization.mdx | MCP Authorization Specification}
14
- * @module mcp-server/transports/authentication/authMiddleware
14
+ * @module src/mcp-server/transports/authentication/authMiddleware
15
15
  */
16
16
  import { NextFunction, Request, Response } from "express";
17
17
  import { AuthInfo } from "@modelcontextprotocol/sdk/server/auth/types.js";
@@ -11,7 +11,7 @@
11
11
  * If the token is missing, invalid, or expired, it sends an HTTP 401 Unauthorized response.
12
12
  *
13
13
  * @see {@link https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-03-26/basic/authorization.mdx | MCP Authorization Specification}
14
- * @module mcp-server/transports/authentication/authMiddleware
14
+ * @module src/mcp-server/transports/authentication/authMiddleware
15
15
  */
16
16
  import jwt from "jsonwebtoken";
17
17
  import { config, environment } from "../../../config/index.js";
@@ -45,7 +45,10 @@ export function mcpAuthMiddleware(req, res, next) {
45
45
  scopes: ["dev-scope"],
46
46
  };
47
47
  // Log dev mode details separately, not attaching to req.auth if not part of AuthInfo
48
- logger.debug("Dev mode auth object created.", { ...context, authDetails: req.auth });
48
+ logger.debug("Dev mode auth object created.", {
49
+ ...context,
50
+ authDetails: req.auth,
51
+ });
49
52
  return next();
50
53
  }
51
54
  else {
@@ -67,7 +70,9 @@ export function mcpAuthMiddleware(req, res, next) {
67
70
  const tokenParts = authHeader.split(" ");
68
71
  if (tokenParts.length !== 2 || tokenParts[0] !== "Bearer" || !tokenParts[1]) {
69
72
  logger.warning("Authentication failed: Malformed Bearer token.", context);
70
- res.status(401).json({ error: "Unauthorized: Malformed authentication token." });
73
+ res
74
+ .status(401)
75
+ .json({ error: "Unauthorized: Malformed authentication token." });
71
76
  return;
72
77
  }
73
78
  const rawToken = tokenParts[1];
@@ -75,26 +80,37 @@ export function mcpAuthMiddleware(req, res, next) {
75
80
  const decoded = jwt.verify(rawToken, config.mcpAuthSecretKey);
76
81
  if (typeof decoded === "string") {
77
82
  logger.warning("Authentication failed: JWT decoded to a string, expected an object payload.", context);
78
- res.status(401).json({ error: "Unauthorized: Invalid token payload format." });
83
+ res
84
+ .status(401)
85
+ .json({ error: "Unauthorized: Invalid token payload format." });
79
86
  return;
80
87
  }
81
88
  // Extract and validate fields for SDK's AuthInfo
82
- const clientIdFromToken = typeof decoded.cid === 'string' ? decoded.cid : (typeof decoded.client_id === 'string' ? decoded.client_id : undefined);
89
+ const clientIdFromToken = typeof decoded.cid === "string"
90
+ ? decoded.cid
91
+ : typeof decoded.client_id === "string"
92
+ ? decoded.client_id
93
+ : undefined;
83
94
  if (!clientIdFromToken) {
84
95
  logger.warning("Authentication failed: JWT 'cid' or 'client_id' claim is missing or not a string.", { ...context, jwtPayloadKeys: Object.keys(decoded) });
85
- res.status(401).json({ error: "Unauthorized: Invalid token, missing client identifier." });
96
+ res.status(401).json({
97
+ error: "Unauthorized: Invalid token, missing client identifier.",
98
+ });
86
99
  return;
87
100
  }
88
101
  let scopesFromToken;
89
- if (Array.isArray(decoded.scp) && decoded.scp.every(s => typeof s === 'string')) {
102
+ if (Array.isArray(decoded.scp) &&
103
+ decoded.scp.every((s) => typeof s === "string")) {
90
104
  scopesFromToken = decoded.scp;
91
105
  }
92
- else if (typeof decoded.scope === 'string' && decoded.scope.trim() !== '') {
93
- scopesFromToken = decoded.scope.split(' ').filter(s => s);
94
- if (scopesFromToken.length === 0 && decoded.scope.trim() !== '') { // handles case " " -> [""]
106
+ else if (typeof decoded.scope === "string" &&
107
+ decoded.scope.trim() !== "") {
108
+ scopesFromToken = decoded.scope.split(" ").filter((s) => s);
109
+ if (scopesFromToken.length === 0 && decoded.scope.trim() !== "") {
110
+ // handles case " " -> [""]
95
111
  scopesFromToken = [decoded.scope.trim()];
96
112
  }
97
- else if (scopesFromToken.length === 0 && decoded.scope.trim() === '') {
113
+ else if (scopesFromToken.length === 0 && decoded.scope.trim() === "") {
98
114
  // If scope is an empty string, treat as no scopes rather than erroring, or use a default.
99
115
  // Depending on strictness, could also error here. For now, allow empty array if scope was empty string.
100
116
  logger.debug("JWT 'scope' claim was an empty string, resulting in empty scopes array.", context);
@@ -116,15 +132,23 @@ export function mcpAuthMiddleware(req, res, next) {
116
132
  scopes: scopesFromToken,
117
133
  };
118
134
  // Log separately if other JWT claims like 'sub' (sessionId) are needed for app logic
119
- const subClaimForLogging = typeof decoded.sub === 'string' ? decoded.sub : undefined;
120
- logger.debug("JWT verified successfully. AuthInfo attached to request.", { ...context, mcpSessionIdContext: subClaimForLogging, clientId: req.auth.clientId, scopes: req.auth.scopes });
135
+ const subClaimForLogging = typeof decoded.sub === "string" ? decoded.sub : undefined;
136
+ logger.debug("JWT verified successfully. AuthInfo attached to request.", {
137
+ ...context,
138
+ mcpSessionIdContext: subClaimForLogging,
139
+ clientId: req.auth.clientId,
140
+ scopes: req.auth.scopes,
141
+ });
121
142
  next();
122
143
  }
123
144
  catch (error) {
124
145
  let errorMessage = "Invalid token";
125
146
  if (error instanceof jwt.TokenExpiredError) {
126
147
  errorMessage = "Token expired";
127
- logger.warning("Authentication failed: Token expired.", { ...context, expiredAt: error.expiredAt });
148
+ logger.warning("Authentication failed: Token expired.", {
149
+ ...context,
150
+ expiredAt: error.expiredAt,
151
+ });
128
152
  }
129
153
  else if (error instanceof jwt.JsonWebTokenError) {
130
154
  errorMessage = `Invalid token: ${error.message}`;
@@ -8,7 +8,7 @@
8
8
  *
9
9
  * Specification Reference:
10
10
  * https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-03-26/basic/transports.mdx#streamable-http
11
- * @module mcp-server/transports/httpTransport
11
+ * @module src/mcp-server/transports/httpTransport
12
12
  */
13
13
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
14
14
  import { RequestContext } from "../../utils/index.js";
@@ -8,7 +8,7 @@
8
8
  *
9
9
  * Specification Reference:
10
10
  * https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-03-26/basic/transports.mdx#streamable-http
11
- * @module mcp-server/transports/httpTransport
11
+ * @module src/mcp-server/transports/httpTransport
12
12
  */
13
13
  import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
14
14
  import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
@@ -15,7 +15,7 @@
15
15
  * controlling the server process. This implementation follows that guideline.
16
16
  *
17
17
  * @see {@link https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-03-26/basic/authorization.mdx | MCP Authorization Specification}
18
- * @module mcp-server/transports/stdioTransport
18
+ * @module src/mcp-server/transports/stdioTransport
19
19
  */
20
20
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
21
21
  import { RequestContext } from "../../utils/index.js";
@@ -15,7 +15,7 @@
15
15
  * controlling the server process. This implementation follows that guideline.
16
16
  *
17
17
  * @see {@link https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-03-26/basic/authorization.mdx | MCP Authorization Specification}
18
- * @module mcp-server/transports/stdioTransport
18
+ * @module src/mcp-server/transports/stdioTransport
19
19
  */
20
20
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
21
21
  import { ErrorHandler, logger } from "../../utils/index.js";
@@ -3,7 +3,7 @@
3
3
  * OpenRouter API, using the OpenAI SDK for chat completions. It handles API key
4
4
  * configuration, default parameters, rate limiting, model-specific parameter adjustments,
5
5
  * and error handling.
6
- * @module services/openRouterProvider
6
+ * @module src/services/openRouterProvider
7
7
  */
8
8
  import OpenAI from "openai";
9
9
  import { config } from "../config/index.js";
@@ -3,7 +3,7 @@
3
3
  * for handling errors within the Model Context Protocol (MCP) server and its components.
4
4
  * This module provides a structured way to represent and communicate errors, ensuring
5
5
  * consistency and clarity for both server-side operations and client-side error handling.
6
- * @module types-global/errors
6
+ * @module src/types-global/errors
7
7
  */
8
8
  import { z } from "zod";
9
9
  /**
@@ -3,7 +3,7 @@
3
3
  * for handling errors within the Model Context Protocol (MCP) server and its components.
4
4
  * This module provides a structured way to represent and communicate errors, ensuring
5
5
  * consistency and clarity for both server-side operations and client-side error handling.
6
- * @module types-global/errors
6
+ * @module src/types-global/errors
7
7
  */
8
8
  import { z } from "zod";
9
9
  /**
@@ -2,9 +2,9 @@
2
2
  * @fileoverview Barrel file for the utils module.
3
3
  * This file re-exports all utilities from their categorized subdirectories,
4
4
  * providing a single entry point for accessing utility functions.
5
- * @module utils
5
+ * @module src/utils
6
6
  */
7
7
  export * from "./internal/index.js";
8
+ export * from "./metrics/index.js";
8
9
  export * from "./parsing/index.js";
9
10
  export * from "./security/index.js";
10
- export * from "./metrics/index.js";
@@ -2,13 +2,13 @@
2
2
  * @fileoverview Barrel file for the utils module.
3
3
  * This file re-exports all utilities from their categorized subdirectories,
4
4
  * providing a single entry point for accessing utility functions.
5
- * @module utils
5
+ * @module src/utils
6
6
  */
7
7
  // Re-export all utilities from their categorized subdirectories
8
8
  export * from "./internal/index.js";
9
+ export * from "./metrics/index.js";
9
10
  export * from "./parsing/index.js";
10
11
  export * from "./security/index.js";
11
- export * from "./metrics/index.js";
12
12
  // It's good practice to have index.ts files in each subdirectory
13
13
  // that export the contents of that directory.
14
14
  // Assuming those will be created or already exist.
@@ -3,7 +3,7 @@
3
3
  * It defines structures for error context, options for handling errors,
4
4
  * and mappings for classifying errors. The main `ErrorHandler` class
5
5
  * offers static methods for consistent error processing, logging, and transformation.
6
- * @module utils/internal/errorHandler
6
+ * @module src/utils/internal/errorHandler
7
7
  */
8
8
  import { BaseErrorCode } from "../../types-global/errors.js";
9
9
  /**
@@ -3,7 +3,7 @@
3
3
  * It defines structures for error context, options for handling errors,
4
4
  * and mappings for classifying errors. The main `ErrorHandler` class
5
5
  * offers static methods for consistent error processing, logging, and transformation.
6
- * @module utils/internal/errorHandler
6
+ * @module src/utils/internal/errorHandler
7
7
  */
8
8
  import { BaseErrorCode, McpError } from "../../types-global/errors.js";
9
9
  import { generateUUID, sanitizeInputForLogging } from "../index.js";
@@ -2,7 +2,7 @@
2
2
  * @fileoverview Barrel file for internal utility modules.
3
3
  * This file re-exports core internal utilities related to error handling,
4
4
  * logging, and request context management.
5
- * @module utils/internal
5
+ * @module src/utils/internal
6
6
  */
7
7
  export * from "./errorHandler.js";
8
8
  export * from "./logger.js";
@@ -2,7 +2,7 @@
2
2
  * @fileoverview Barrel file for internal utility modules.
3
3
  * This file re-exports core internal utilities related to error handling,
4
4
  * logging, and request context management.
5
- * @module utils/internal
5
+ * @module src/utils/internal
6
6
  */
7
7
  export * from "./errorHandler.js";
8
8
  export * from "./logger.js";
@@ -2,7 +2,7 @@
2
2
  * @fileoverview Provides a singleton Logger class that wraps Winston for file logging
3
3
  * and supports sending MCP (Model Context Protocol) `notifications/message`.
4
4
  * It handles different log levels compliant with RFC 5424 and MCP specifications.
5
- * @module utils/internal/logger
5
+ * @module src/utils/internal/logger
6
6
  */
7
7
  import fs from "fs";
8
8
  import path from "path";
@@ -40,7 +40,8 @@ const projectRoot = process.cwd(); // Use current working directory as project r
40
40
  const logsDir = path.join(projectRoot, "logs");
41
41
  // Security check for the logs directory path
42
42
  const resolvedLogsDir = path.resolve(logsDir); // Should be projectRoot/logs
43
- const isLogsDirSafe = resolvedLogsDir.startsWith(projectRoot + path.sep) && resolvedLogsDir !== projectRoot;
43
+ const isLogsDirSafe = resolvedLogsDir.startsWith(projectRoot + path.sep) &&
44
+ resolvedLogsDir !== projectRoot;
44
45
  if (!isLogsDirSafe) {
45
46
  // This case should ideally not be hit if logsDir is simply projectRoot + /logs
46
47
  // But it's a safeguard if path.join or path.resolve behaves unexpectedly or logsDir is manipulated.
@@ -3,7 +3,7 @@
3
3
  * A request context is an object carrying a unique ID, timestamp, and other
4
4
  * relevant data for logging, tracing, and processing. It also defines
5
5
  * configuration and operational context structures.
6
- * @module utils/internal/requestContext
6
+ * @module src/utils/internal/requestContext
7
7
  */
8
8
  /**
9
9
  * Defines the core structure for context information associated with a request or operation.
@@ -3,10 +3,10 @@
3
3
  * A request context is an object carrying a unique ID, timestamp, and other
4
4
  * relevant data for logging, tracing, and processing. It also defines
5
5
  * configuration and operational context structures.
6
- * @module utils/internal/requestContext
6
+ * @module src/utils/internal/requestContext
7
7
  */
8
- import { logger } from "./logger.js";
9
8
  import { generateUUID } from "../index.js";
9
+ import { logger } from "./logger.js";
10
10
  /**
11
11
  * Singleton-like service object for managing request context operations.
12
12
  * @private
@@ -2,6 +2,6 @@
2
2
  * @fileoverview Barrel file for metrics-related utility modules.
3
3
  * This file re-exports utilities for collecting and processing metrics,
4
4
  * such as token counting.
5
- * @module utils/metrics
5
+ * @module src/utils/metrics
6
6
  */
7
7
  export * from "./tokenCounter.js";
@@ -2,6 +2,6 @@
2
2
  * @fileoverview Barrel file for metrics-related utility modules.
3
3
  * This file re-exports utilities for collecting and processing metrics,
4
4
  * such as token counting.
5
- * @module utils/metrics
5
+ * @module src/utils/metrics
6
6
  */
7
7
  export * from "./tokenCounter.js";
@@ -3,7 +3,7 @@
3
3
  * using the `tiktoken` library, specifically configured for 'gpt-4o' tokenization.
4
4
  * These functions are essential for managing token limits and estimating costs
5
5
  * when interacting with language models.
6
- * @module utils/metrics/tokenCounter
6
+ * @module src/utils/metrics/tokenCounter
7
7
  */
8
8
  import { ChatCompletionMessageParam } from "openai/resources/chat/completions";
9
9
  import { RequestContext } from "../index.js";
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @fileoverview Provides utility functions for parsing natural language date strings
3
3
  * into Date objects or detailed parsing results using the `chrono-node` library.
4
- * @module utils/parsing/dateParser
4
+ * @module src/utils/parsing/dateParser
5
5
  */
6
6
  import * as chrono from "chrono-node";
7
7
  import { RequestContext } from "../index.js";
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @fileoverview Provides utility functions for parsing natural language date strings
3
3
  * into Date objects or detailed parsing results using the `chrono-node` library.
4
- * @module utils/parsing/dateParser
4
+ * @module src/utils/parsing/dateParser
5
5
  */
6
6
  import * as chrono from "chrono-node";
7
7
  import { BaseErrorCode } from "../../types-global/errors.js";
@@ -2,7 +2,7 @@
2
2
  * @fileoverview Barrel file for parsing utility modules.
3
3
  * This file re-exports utilities related to parsing various data formats,
4
4
  * such as JSON and dates.
5
- * @module utils/parsing
5
+ * @module src/utils/parsing
6
6
  */
7
- export * from "./jsonParser.js";
8
7
  export * from "./dateParser.js";
8
+ export * from "./jsonParser.js";
@@ -2,7 +2,7 @@
2
2
  * @fileoverview Barrel file for parsing utility modules.
3
3
  * This file re-exports utilities related to parsing various data formats,
4
4
  * such as JSON and dates.
5
- * @module utils/parsing
5
+ * @module src/utils/parsing
6
6
  */
7
- export * from "./jsonParser.js";
8
7
  export * from "./dateParser.js";
8
+ export * from "./jsonParser.js";
@@ -2,7 +2,7 @@
2
2
  * @fileoverview Provides a utility class for parsing potentially partial JSON strings.
3
3
  * It wraps the 'partial-json' npm library and includes functionality to handle
4
4
  * optional <think>...</think> blocks often found at the beginning of LLM outputs.
5
- * @module utils/parsing/jsonParser
5
+ * @module src/utils/parsing/jsonParser
6
6
  */
7
7
  import { parse as parsePartialJson, Allow as PartialJsonAllow, } from "partial-json";
8
8
  import { BaseErrorCode, McpError } from "../../types-global/errors.js";
@@ -61,9 +61,15 @@ export class JsonParser {
61
61
  if (match) {
62
62
  const thinkContent = match[1].trim();
63
63
  const restOfString = match[2];
64
- const logContext = context || requestContextService.createRequestContext({ operation: "JsonParser.thinkBlock" });
64
+ const logContext = context ||
65
+ requestContextService.createRequestContext({
66
+ operation: "JsonParser.thinkBlock",
67
+ });
65
68
  if (thinkContent) {
66
- logger.debug("LLM <think> block detected and logged.", { ...logContext, thinkContent });
69
+ logger.debug("LLM <think> block detected and logged.", {
70
+ ...logContext,
71
+ thinkContent,
72
+ });
67
73
  }
68
74
  else {
69
75
  logger.debug("Empty LLM <think> block detected.", logContext);
@@ -78,7 +84,10 @@ export class JsonParser {
78
84
  return parsePartialJson(stringToParse, allowPartial);
79
85
  }
80
86
  catch (error) {
81
- const errorLogContext = context || requestContextService.createRequestContext({ operation: "JsonParser.parseError" });
87
+ const errorLogContext = context ||
88
+ requestContextService.createRequestContext({
89
+ operation: "JsonParser.parseError",
90
+ });
82
91
  logger.error("Failed to parse JSON content.", {
83
92
  ...errorLogContext,
84
93
  errorDetails: error.message,
@@ -7,9 +7,9 @@
7
7
  * with the `requestContextService`, which itself uses `generateUUID` from this module.
8
8
  * This was causing `ReferenceError: Cannot access 'generateUUID' before initialization`
9
9
  * during application startup.
10
- * @module utils/security/idGenerator
10
+ * @module src/utils/security/idGenerator
11
11
  */
12
- import { randomBytes, randomUUID as cryptoRandomUUID } from "crypto";
12
+ import { randomUUID as cryptoRandomUUID, randomBytes } from "crypto";
13
13
  import { BaseErrorCode, McpError } from "../../types-global/errors.js";
14
14
  /**
15
15
  * A generic ID Generator class for creating and managing unique, prefixed identifiers.
@@ -77,7 +77,9 @@ export class IdGenerator {
77
77
  // Logging removed.
78
78
  const { length = IdGenerator.DEFAULT_LENGTH, separator = IdGenerator.DEFAULT_SEPARATOR, charset = IdGenerator.DEFAULT_CHARSET, } = options;
79
79
  const randomPart = this.generateRandomString(length, charset);
80
- const generatedId = prefix ? `${prefix}${separator}${randomPart}` : randomPart;
80
+ const generatedId = prefix
81
+ ? `${prefix}${separator}${randomPart}`
82
+ : randomPart;
81
83
  return generatedId;
82
84
  }
83
85
  /**
@@ -2,8 +2,8 @@
2
2
  * @fileoverview Barrel file for security-related utility modules.
3
3
  * This file re-exports utilities for input sanitization, rate limiting,
4
4
  * and ID generation.
5
- * @module utils/security
5
+ * @module src/utils/security
6
6
  */
7
- export * from "./sanitization.js";
8
- export * from "./rateLimiter.js";
9
7
  export * from "./idGenerator.js";
8
+ export * from "./rateLimiter.js";
9
+ export * from "./sanitization.js";
@@ -2,8 +2,8 @@
2
2
  * @fileoverview Barrel file for security-related utility modules.
3
3
  * This file re-exports utilities for input sanitization, rate limiting,
4
4
  * and ID generation.
5
- * @module utils/security
5
+ * @module src/utils/security
6
6
  */
7
- export * from "./sanitization.js";
8
- export * from "./rateLimiter.js";
9
7
  export * from "./idGenerator.js";
8
+ export * from "./rateLimiter.js";
9
+ export * from "./sanitization.js";
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @fileoverview Provides a generic `RateLimiter` class for implementing rate limiting logic.
3
3
  * It supports configurable time windows, request limits, and automatic cleanup of expired entries.
4
- * @module utils/security/rateLimiter
4
+ * @module src/utils/security/rateLimiter
5
5
  */
6
6
  import { environment } from "../../config/index.js";
7
7
  import { BaseErrorCode, McpError } from "../../types-global/errors.js";
@@ -2,7 +2,7 @@
2
2
  * @fileoverview Provides a comprehensive `Sanitization` class for various input cleaning and validation tasks.
3
3
  * This module includes utilities for sanitizing HTML, strings, URLs, file paths, JSON, numbers,
4
4
  * and for redacting sensitive information from data intended for logging.
5
- * @module utils/security/sanitization
5
+ * @module src/utils/security/sanitization
6
6
  */
7
7
  import path from "path";
8
8
  import sanitizeHtml from "sanitize-html";
@@ -22,8 +22,19 @@ export class Sanitization {
22
22
  * @private
23
23
  */
24
24
  this.sensitiveFields = [
25
- "password", "token", "secret", "key", "apiKey", "auth",
26
- "credential", "jwt", "ssn", "credit", "card", "cvv", "authorization",
25
+ "password",
26
+ "token",
27
+ "secret",
28
+ "key",
29
+ "apiKey",
30
+ "auth",
31
+ "credential",
32
+ "jwt",
33
+ "ssn",
34
+ "credit",
35
+ "card",
36
+ "cvv",
37
+ "authorization",
27
38
  ];
28
39
  /**
29
40
  * Default configuration for HTML sanitization.
@@ -31,9 +42,33 @@ export class Sanitization {
31
42
  */
32
43
  this.defaultHtmlSanitizeConfig = {
33
44
  allowedTags: [
34
- "h1", "h2", "h3", "h4", "h5", "h6", "p", "a", "ul", "ol", "li",
35
- "b", "i", "strong", "em", "strike", "code", "hr", "br", "div",
36
- "table", "thead", "tbody", "tr", "th", "td", "pre",
45
+ "h1",
46
+ "h2",
47
+ "h3",
48
+ "h4",
49
+ "h5",
50
+ "h6",
51
+ "p",
52
+ "a",
53
+ "ul",
54
+ "ol",
55
+ "li",
56
+ "b",
57
+ "i",
58
+ "strong",
59
+ "em",
60
+ "strike",
61
+ "code",
62
+ "hr",
63
+ "br",
64
+ "div",
65
+ "table",
66
+ "thead",
67
+ "tbody",
68
+ "tr",
69
+ "th",
70
+ "td",
71
+ "pre",
37
72
  ],
38
73
  allowedAttributes: {
39
74
  a: ["href", "name", "target"],
@@ -120,13 +155,23 @@ export class Sanitization {
120
155
  case "attribute":
121
156
  return sanitizeHtml(input, { allowedTags: [], allowedAttributes: {} });
122
157
  case "url":
123
- if (!validator.isURL(input, { protocols: ["http", "https"], require_protocol: true, require_host: true })) {
124
- logger.warning("Potentially invalid URL detected during string sanitization (context: url)", requestContextService.createRequestContext({ operation: "Sanitization.sanitizeString.urlWarning", invalidUrlAttempt: input }));
158
+ if (!validator.isURL(input, {
159
+ protocols: ["http", "https"],
160
+ require_protocol: true,
161
+ require_host: true,
162
+ })) {
163
+ logger.warning("Potentially invalid URL detected during string sanitization (context: url)", requestContextService.createRequestContext({
164
+ operation: "Sanitization.sanitizeString.urlWarning",
165
+ invalidUrlAttempt: input,
166
+ }));
125
167
  return "";
126
168
  }
127
169
  return validator.trim(input);
128
170
  case "javascript":
129
- logger.error("Attempted JavaScript sanitization via sanitizeString, which is disallowed.", requestContextService.createRequestContext({ operation: "Sanitization.sanitizeString.jsAttempt", inputSnippet: input.substring(0, 50) }));
171
+ logger.error("Attempted JavaScript sanitization via sanitizeString, which is disallowed.", requestContextService.createRequestContext({
172
+ operation: "Sanitization.sanitizeString.jsAttempt",
173
+ inputSnippet: input.substring(0, 50),
174
+ }));
130
175
  throw new McpError(BaseErrorCode.VALIDATION_ERROR, "JavaScript sanitization is not supported through sanitizeString due to security risks.");
131
176
  case "text":
132
177
  default:
@@ -152,7 +197,11 @@ export class Sanitization {
152
197
  sanitizeUrl(input, allowedProtocols = ["http", "https"]) {
153
198
  try {
154
199
  const trimmedInput = input.trim();
155
- if (!validator.isURL(trimmedInput, { protocols: allowedProtocols, require_protocol: true, require_host: true })) {
200
+ if (!validator.isURL(trimmedInput, {
201
+ protocols: allowedProtocols,
202
+ require_protocol: true,
203
+ require_host: true,
204
+ })) {
156
205
  throw new Error("Invalid URL format or protocol not in allowed list.");
157
206
  }
158
207
  if (trimmedInput.toLowerCase().startsWith("javascript:")) {
@@ -161,7 +210,9 @@ export class Sanitization {
161
210
  return trimmedInput;
162
211
  }
163
212
  catch (error) {
164
- throw new McpError(BaseErrorCode.VALIDATION_ERROR, error instanceof Error ? error.message : "Invalid or unsafe URL provided.", { input });
213
+ throw new McpError(BaseErrorCode.VALIDATION_ERROR, error instanceof Error
214
+ ? error.message
215
+ : "Invalid or unsafe URL provided.", { input });
165
216
  }
166
217
  }
167
218
  /**
@@ -193,12 +244,15 @@ export class Sanitization {
193
244
  let finalSanitizedPath;
194
245
  if (effectiveOptions.rootDir) {
195
246
  const fullPath = path.resolve(effectiveOptions.rootDir, normalized);
196
- if (!fullPath.startsWith(effectiveOptions.rootDir + path.sep) && fullPath !== effectiveOptions.rootDir) {
247
+ if (!fullPath.startsWith(effectiveOptions.rootDir + path.sep) &&
248
+ fullPath !== effectiveOptions.rootDir) {
197
249
  throw new Error("Path traversal detected: attempts to escape the defined root directory.");
198
250
  }
199
251
  finalSanitizedPath = path.relative(effectiveOptions.rootDir, fullPath);
200
- finalSanitizedPath = finalSanitizedPath === "" ? "." : finalSanitizedPath;
201
- if (path.isAbsolute(finalSanitizedPath) && !effectiveOptions.allowAbsolute) {
252
+ finalSanitizedPath =
253
+ finalSanitizedPath === "" ? "." : finalSanitizedPath;
254
+ if (path.isAbsolute(finalSanitizedPath) &&
255
+ !effectiveOptions.allowAbsolute) {
202
256
  throw new Error("Path resolved to absolute outside root when absolute paths are disallowed.");
203
257
  }
204
258
  }
@@ -215,7 +269,8 @@ export class Sanitization {
215
269
  else {
216
270
  const resolvedAgainstCwd = path.resolve(normalized);
217
271
  const currentWorkingDir = path.resolve(".");
218
- if (!resolvedAgainstCwd.startsWith(currentWorkingDir + path.sep) && resolvedAgainstCwd !== currentWorkingDir) {
272
+ if (!resolvedAgainstCwd.startsWith(currentWorkingDir + path.sep) &&
273
+ resolvedAgainstCwd !== currentWorkingDir) {
219
274
  throw new Error("Relative path traversal detected (escapes current working directory context).");
220
275
  }
221
276
  finalSanitizedPath = normalized;
@@ -225,7 +280,9 @@ export class Sanitization {
225
280
  sanitizedPath: finalSanitizedPath,
226
281
  originalInput,
227
282
  wasAbsolute: wasAbsoluteInitially,
228
- convertedToRelative: wasAbsoluteInitially && !path.isAbsolute(finalSanitizedPath) && !effectiveOptions.allowAbsolute,
283
+ convertedToRelative: wasAbsoluteInitially &&
284
+ !path.isAbsolute(finalSanitizedPath) &&
285
+ !effectiveOptions.allowAbsolute,
229
286
  optionsUsed: effectiveOptions,
230
287
  };
231
288
  }
@@ -236,7 +293,9 @@ export class Sanitization {
236
293
  pathOptionsUsed: effectiveOptions,
237
294
  errorMessage: error instanceof Error ? error.message : String(error),
238
295
  }));
239
- throw new McpError(BaseErrorCode.VALIDATION_ERROR, error instanceof Error ? error.message : "Invalid or unsafe path provided.", { input: originalInput });
296
+ throw new McpError(BaseErrorCode.VALIDATION_ERROR, error instanceof Error
297
+ ? error.message
298
+ : "Invalid or unsafe path provided.", { input: originalInput });
240
299
  }
241
300
  }
242
301
  /**
@@ -260,7 +319,9 @@ export class Sanitization {
260
319
  catch (error) {
261
320
  if (error instanceof McpError)
262
321
  throw error;
263
- throw new McpError(BaseErrorCode.VALIDATION_ERROR, error instanceof Error ? error.message : "Invalid JSON format.", { inputPreview: input.length > 100 ? `${input.substring(0, 100)}...` : input });
322
+ throw new McpError(BaseErrorCode.VALIDATION_ERROR, error instanceof Error ? error.message : "Invalid JSON format.", {
323
+ inputPreview: input.length > 100 ? `${input.substring(0, 100)}...` : input,
324
+ });
264
325
  }
265
326
  }
266
327
  /**
@@ -324,7 +385,9 @@ export class Sanitization {
324
385
  try {
325
386
  if (!input || typeof input !== "object")
326
387
  return input;
327
- const clonedInput = typeof structuredClone === "function" ? structuredClone(input) : JSON.parse(JSON.stringify(input));
388
+ const clonedInput = typeof structuredClone === "function"
389
+ ? structuredClone(input)
390
+ : JSON.parse(JSON.stringify(input));
328
391
  this.redactSensitiveFields(clonedInput);
329
392
  return clonedInput;
330
393
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-ts-template",
3
- "version": "1.2.2",
3
+ "version": "1.2.3",
4
4
  "description": "TypeScript template for building Model Context Protocol (MCP) servers & clients. Features production-ready utilities, stdio/HTTP transports (with JWT auth), examples, and type safety. Ideal starting point for creating MCP-based applications.",
5
5
  "main": "dist/index.js",
6
6
  "files": [