@cyanheads/git-mcp-server 2.0.1 → 2.0.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 (99) hide show
  1. package/README.md +55 -89
  2. package/{build → dist}/config/index.js +16 -18
  3. package/{build → dist}/index.js +80 -30
  4. package/dist/mcp-server/server.js +296 -0
  5. package/{build → dist}/mcp-server/tools/gitAdd/logic.js +9 -6
  6. package/{build → dist}/mcp-server/tools/gitAdd/registration.js +7 -4
  7. package/{build → dist}/mcp-server/tools/gitBranch/logic.js +23 -12
  8. package/{build → dist}/mcp-server/tools/gitBranch/registration.js +8 -5
  9. package/{build → dist}/mcp-server/tools/gitCheckout/logic.js +92 -44
  10. package/{build → dist}/mcp-server/tools/gitCheckout/registration.js +8 -5
  11. package/{build → dist}/mcp-server/tools/gitCherryPick/logic.js +10 -7
  12. package/{build → dist}/mcp-server/tools/gitCherryPick/registration.js +8 -5
  13. package/{build → dist}/mcp-server/tools/gitClean/logic.js +9 -6
  14. package/{build → dist}/mcp-server/tools/gitClean/registration.js +8 -5
  15. package/{build → dist}/mcp-server/tools/gitClearWorkingDir/logic.js +3 -2
  16. package/{build → dist}/mcp-server/tools/gitClearWorkingDir/registration.js +7 -4
  17. package/{build → dist}/mcp-server/tools/gitClone/logic.js +8 -5
  18. package/{build → dist}/mcp-server/tools/gitClone/registration.js +7 -4
  19. package/dist/mcp-server/tools/gitCommit/logic.js +207 -0
  20. package/{build → dist}/mcp-server/tools/gitCommit/registration.js +22 -15
  21. package/{build → dist}/mcp-server/tools/gitDiff/logic.js +9 -6
  22. package/{build → dist}/mcp-server/tools/gitDiff/registration.js +8 -5
  23. package/{build → dist}/mcp-server/tools/gitFetch/logic.js +10 -7
  24. package/{build → dist}/mcp-server/tools/gitFetch/registration.js +8 -5
  25. package/{build → dist}/mcp-server/tools/gitInit/index.js +2 -2
  26. package/{build → dist}/mcp-server/tools/gitInit/logic.js +9 -6
  27. package/dist/mcp-server/tools/gitInit/registration.js +98 -0
  28. package/{build → dist}/mcp-server/tools/gitLog/logic.js +53 -16
  29. package/{build → dist}/mcp-server/tools/gitLog/registration.js +8 -5
  30. package/{build → dist}/mcp-server/tools/gitMerge/logic.js +9 -6
  31. package/{build → dist}/mcp-server/tools/gitMerge/registration.js +8 -5
  32. package/{build → dist}/mcp-server/tools/gitPull/logic.js +11 -8
  33. package/{build → dist}/mcp-server/tools/gitPull/registration.js +7 -4
  34. package/{build → dist}/mcp-server/tools/gitPush/logic.js +12 -9
  35. package/{build → dist}/mcp-server/tools/gitPush/registration.js +7 -4
  36. package/{build → dist}/mcp-server/tools/gitRebase/logic.js +9 -6
  37. package/{build → dist}/mcp-server/tools/gitRebase/registration.js +8 -5
  38. package/{build → dist}/mcp-server/tools/gitRemote/logic.js +4 -5
  39. package/{build → dist}/mcp-server/tools/gitRemote/registration.js +2 -4
  40. package/{build → dist}/mcp-server/tools/gitReset/logic.js +5 -6
  41. package/{build → dist}/mcp-server/tools/gitReset/registration.js +2 -4
  42. package/{build → dist}/mcp-server/tools/gitSetWorkingDir/logic.js +5 -6
  43. package/{build → dist}/mcp-server/tools/gitSetWorkingDir/registration.js +22 -13
  44. package/{build → dist}/mcp-server/tools/gitShow/logic.js +5 -6
  45. package/{build → dist}/mcp-server/tools/gitShow/registration.js +3 -5
  46. package/{build → dist}/mcp-server/tools/gitStash/logic.js +5 -6
  47. package/{build → dist}/mcp-server/tools/gitStash/registration.js +3 -5
  48. package/{build → dist}/mcp-server/tools/gitStatus/logic.js +5 -6
  49. package/{build → dist}/mcp-server/tools/gitStatus/registration.js +2 -4
  50. package/{build → dist}/mcp-server/tools/gitTag/logic.js +3 -4
  51. package/{build → dist}/mcp-server/tools/gitTag/registration.js +2 -4
  52. package/dist/mcp-server/transports/authentication/authMiddleware.js +145 -0
  53. package/dist/mcp-server/transports/httpTransport.js +432 -0
  54. package/dist/mcp-server/transports/stdioTransport.js +87 -0
  55. package/{build → dist}/types-global/errors.js +2 -2
  56. package/dist/utils/index.js +12 -0
  57. package/{build/utils → dist/utils/internal}/errorHandler.js +18 -8
  58. package/dist/utils/internal/index.js +3 -0
  59. package/dist/utils/internal/logger.js +254 -0
  60. package/{build/utils → dist/utils/internal}/requestContext.js +2 -3
  61. package/dist/utils/metrics/index.js +1 -0
  62. package/{build/utils → dist/utils/metrics}/tokenCounter.js +3 -3
  63. package/dist/utils/parsing/dateParser.js +62 -0
  64. package/dist/utils/parsing/index.js +2 -0
  65. package/{build/utils → dist/utils/parsing}/jsonParser.js +3 -2
  66. package/{build/utils → dist/utils/security}/idGenerator.js +4 -5
  67. package/dist/utils/security/index.js +3 -0
  68. package/{build/utils → dist/utils/security}/rateLimiter.js +7 -10
  69. package/{build/utils → dist/utils/security}/sanitization.js +4 -3
  70. package/package.json +20 -16
  71. package/build/mcp-server/server.js +0 -572
  72. package/build/mcp-server/tools/gitCommit/logic.js +0 -129
  73. package/build/mcp-server/tools/gitInit/registration.js +0 -44
  74. package/build/types-global/mcp.js +0 -59
  75. package/build/types-global/tool.js +0 -1
  76. package/build/utils/index.js +0 -11
  77. package/build/utils/logger.js +0 -266
  78. /package/{build → dist}/mcp-server/tools/gitAdd/index.js +0 -0
  79. /package/{build → dist}/mcp-server/tools/gitBranch/index.js +0 -0
  80. /package/{build → dist}/mcp-server/tools/gitCheckout/index.js +0 -0
  81. /package/{build → dist}/mcp-server/tools/gitCherryPick/index.js +0 -0
  82. /package/{build → dist}/mcp-server/tools/gitClean/index.js +0 -0
  83. /package/{build → dist}/mcp-server/tools/gitClearWorkingDir/index.js +0 -0
  84. /package/{build → dist}/mcp-server/tools/gitClone/index.js +0 -0
  85. /package/{build → dist}/mcp-server/tools/gitCommit/index.js +0 -0
  86. /package/{build → dist}/mcp-server/tools/gitDiff/index.js +0 -0
  87. /package/{build → dist}/mcp-server/tools/gitFetch/index.js +0 -0
  88. /package/{build → dist}/mcp-server/tools/gitLog/index.js +0 -0
  89. /package/{build → dist}/mcp-server/tools/gitMerge/index.js +0 -0
  90. /package/{build → dist}/mcp-server/tools/gitPull/index.js +0 -0
  91. /package/{build → dist}/mcp-server/tools/gitPush/index.js +0 -0
  92. /package/{build → dist}/mcp-server/tools/gitRebase/index.js +0 -0
  93. /package/{build → dist}/mcp-server/tools/gitRemote/index.js +0 -0
  94. /package/{build → dist}/mcp-server/tools/gitReset/index.js +0 -0
  95. /package/{build → dist}/mcp-server/tools/gitSetWorkingDir/index.js +0 -0
  96. /package/{build → dist}/mcp-server/tools/gitShow/index.js +0 -0
  97. /package/{build → dist}/mcp-server/tools/gitStash/index.js +0 -0
  98. /package/{build → dist}/mcp-server/tools/gitStatus/index.js +0 -0
  99. /package/{build → dist}/mcp-server/tools/gitTag/index.js +0 -0
@@ -1,14 +1,13 @@
1
- import { z } from 'zod';
2
- import { promisify } from 'util';
3
1
  import { exec } from 'child_process';
4
- import { logger } from '../../../utils/logger.js';
5
- import { McpError, BaseErrorCode } from '../../../types-global/errors.js';
6
- import { sanitization } from '../../../utils/sanitization.js';
2
+ import { promisify } from 'util';
3
+ import { z } from 'zod';
4
+ import { BaseErrorCode, McpError } from '../../../types-global/errors.js'; // Direct import for types-global
5
+ import { logger, sanitization } from '../../../utils/index.js'; // logger (./utils/internal/logger.js), RequestContext (./utils/internal/requestContext.js), sanitization (./utils/security/sanitization.js)
7
6
  const execAsync = promisify(exec);
8
7
  // Define the input schema for the git_show tool using Zod
9
8
  // No refinements needed here, so we don't need a separate BaseSchema
10
9
  export const GitShowInputSchema = z.object({
11
- path: z.string().min(1).optional().default('.').describe("Path to the local Git repository. If omitted, defaults to the path set by `git_set_working_dir` for the current session, or the server's CWD if no session path is set."),
10
+ path: z.string().min(1).optional().default('.').describe("Path to the local Git repository. Defaults to the directory set via `git_set_working_dir` for the session; set 'git_set_working_dir' if not set."),
12
11
  ref: z.string().min(1).describe("The object reference (commit hash, tag name, branch name, HEAD, etc.) to show."),
13
12
  filePath: z.string().optional().describe("Optional specific file path within the ref to show (e.g., show a file's content at a specific commit). If provided, use the format '<ref>:<filePath>'."),
14
13
  // format: z.string().optional().describe("Optional format string for the output"), // Consider adding later
@@ -1,9 +1,7 @@
1
- import { logger } from '../../../utils/logger.js';
2
- import { ErrorHandler } from '../../../utils/errorHandler.js';
3
- import { requestContextService } from '../../../utils/requestContext.js';
1
+ import { BaseErrorCode } from '../../../types-global/errors.js'; // Direct import for types-global
2
+ import { ErrorHandler, logger, requestContextService } from '../../../utils/index.js'; // logger (./utils/internal/logger.js), ErrorHandler (./utils/internal/errorHandler.js), requestContextService (./utils/internal/requestContext.js)
4
3
  // Import the schema and types
5
- import { gitShowLogic, GitShowInputSchema } from './logic.js';
6
- import { BaseErrorCode } from '../../../types-global/errors.js';
4
+ import { GitShowInputSchema, gitShowLogic } from './logic.js';
7
5
  let _getWorkingDirectory;
8
6
  let _getSessionId;
9
7
  /**
@@ -1,13 +1,12 @@
1
- import { z } from 'zod';
2
- import { promisify } from 'util';
3
1
  import { exec } from 'child_process';
4
- import { logger } from '../../../utils/logger.js';
5
- import { McpError, BaseErrorCode } from '../../../types-global/errors.js';
6
- import { sanitization } from '../../../utils/sanitization.js';
2
+ import { promisify } from 'util';
3
+ import { z } from 'zod';
4
+ import { BaseErrorCode, McpError } from '../../../types-global/errors.js'; // Direct import for types-global
5
+ import { logger, sanitization } from '../../../utils/index.js'; // logger (./utils/internal/logger.js), RequestContext (./utils/internal/requestContext.js), sanitization (./utils/security/sanitization.js)
7
6
  const execAsync = promisify(exec);
8
7
  // Define the BASE input schema for the git_stash tool using Zod
9
8
  export const GitStashBaseSchema = z.object({
10
- path: z.string().min(1).optional().default('.').describe("Path to the local Git repository. If omitted, defaults to the path set by `git_set_working_dir` for the current session, or the server's CWD if no session path is set."),
9
+ path: z.string().min(1).optional().default('.').describe("Path to the local Git repository. Defaults to the directory set via `git_set_working_dir` for the session; set 'git_set_working_dir' if not set."),
11
10
  mode: z.enum(['list', 'apply', 'pop', 'drop', 'save']).describe("The stash operation to perform: 'list', 'apply', 'pop', 'drop', 'save'."),
12
11
  stashRef: z.string().optional().describe("Stash reference (e.g., 'stash@{1}'). Required for 'apply', 'pop', 'drop' modes."),
13
12
  message: z.string().optional().describe("Optional descriptive message used only for 'save' mode."),
@@ -1,10 +1,8 @@
1
- import { logger } from '../../../utils/logger.js';
2
- import { ErrorHandler } from '../../../utils/errorHandler.js';
3
- import { requestContextService } from '../../../utils/requestContext.js';
1
+ import { BaseErrorCode } from '../../../types-global/errors.js'; // Direct import for types-global
2
+ import { ErrorHandler, logger, requestContextService } from '../../../utils/index.js'; // logger (./utils/internal/logger.js), ErrorHandler (./utils/internal/errorHandler.js), requestContextService (./utils/internal/requestContext.js)
4
3
  // Import the final schema and types for handler logic
5
4
  // Import the BASE schema separately for registration shape
6
- import { gitStashLogic, GitStashBaseSchema } from './logic.js';
7
- import { BaseErrorCode } from '../../../types-global/errors.js';
5
+ import { GitStashBaseSchema, gitStashLogic } from './logic.js';
8
6
  let _getWorkingDirectory;
9
7
  let _getSessionId;
10
8
  /**
@@ -1,13 +1,12 @@
1
- import { z } from 'zod';
2
- import { promisify } from 'util';
3
1
  import { exec } from 'child_process';
4
- import { logger } from '../../../utils/logger.js';
5
- import { McpError, BaseErrorCode } from '../../../types-global/errors.js';
6
- import { sanitization } from '../../../utils/sanitization.js';
2
+ import { promisify } from 'util';
3
+ import { z } from 'zod';
4
+ import { BaseErrorCode, McpError } from '../../../types-global/errors.js'; // Direct import for types-global
5
+ import { logger, sanitization } from '../../../utils/index.js'; // logger (./utils/internal/logger.js), RequestContext (./utils/internal/requestContext.js), sanitization (./utils/security/sanitization.js)
7
6
  const execAsync = promisify(exec);
8
7
  // Define the input schema for the git_status tool using Zod
9
8
  export const GitStatusInputSchema = z.object({
10
- path: z.string().min(1).optional().default('.').describe("Path to the Git repository (defaults to '.' which uses the session's working directory if set)"),
9
+ path: z.string().min(1).optional().default('.').describe("Path to the Git repository. Defaults to the directory set via `git_set_working_dir` for the session; set 'git_set_working_dir' if not set."),
11
10
  });
12
11
  /**
13
12
  * Parses the output of 'git status --porcelain=v1 -b'.
@@ -1,9 +1,7 @@
1
- import { logger } from '../../../utils/logger.js';
2
- import { ErrorHandler } from '../../../utils/errorHandler.js';
3
- import { requestContextService } from '../../../utils/requestContext.js';
1
+ import { BaseErrorCode } from '../../../types-global/errors.js'; // Direct import for types-global
2
+ import { ErrorHandler, logger, requestContextService } from '../../../utils/index.js'; // logger (./utils/internal/logger.js), ErrorHandler (./utils/internal/errorHandler.js), requestContextService (./utils/internal/requestContext.js)
4
3
  // Import the result type along with the function and input schema
5
4
  import { getGitStatus, GitStatusInputSchema } from './logic.js';
6
- import { BaseErrorCode } from '../../../types-global/errors.js'; // Import BaseErrorCode
7
5
  let _getWorkingDirectory;
8
6
  let _getSessionId;
9
7
  /**
@@ -1,14 +1,13 @@
1
1
  import { exec } from 'child_process';
2
2
  import { promisify } from 'util';
3
3
  import { z } from 'zod';
4
- import { BaseErrorCode, McpError } from '../../../types-global/errors.js';
5
- import { logger } from '../../../utils/logger.js';
6
- import { sanitization } from '../../../utils/sanitization.js';
4
+ import { BaseErrorCode, McpError } from '../../../types-global/errors.js'; // Direct import for types-global
5
+ import { logger, sanitization } from '../../../utils/index.js'; // logger (./utils/internal/logger.js), RequestContext (./utils/internal/requestContext.js), sanitization (./utils/security/sanitization.js)
7
6
  const execAsync = promisify(exec);
8
7
  // Define the base input schema for the git_tag tool using Zod
9
8
  // We export this separately to access its .shape for registration
10
9
  export const GitTagBaseSchema = z.object({
11
- path: z.string().min(1).optional().default('.').describe("Path to the local Git repository. If omitted, defaults to the path set by `git_set_working_dir` for the current session, or the server's CWD if no session path is set."),
10
+ path: z.string().min(1).optional().default('.').describe("Path to the local Git repository. Defaults to the directory set via `git_set_working_dir` for the session; set 'git_set_working_dir' if not set."),
12
11
  mode: z.enum(['list', 'create', 'delete']).describe("The tag operation to perform: 'list' (show all tags), 'create' (add a new tag), 'delete' (remove a local tag)."),
13
12
  tagName: z.string().min(1).optional().describe("The name for the tag. Required for 'create' and 'delete' modes. e.g., 'v2.3.0'."),
14
13
  message: z.string().optional().describe("The annotation message for the tag. Required and used only when 'mode' is 'create' and 'annotate' is true."),
@@ -1,9 +1,7 @@
1
- import { ErrorHandler } from '../../../utils/errorHandler.js';
2
- import { logger } from '../../../utils/logger.js';
3
- import { requestContextService } from '../../../utils/requestContext.js';
1
+ import { BaseErrorCode } from '../../../types-global/errors.js'; // Direct import for types-global
2
+ import { ErrorHandler, logger, requestContextService } from '../../../utils/index.js'; // ErrorHandler (./utils/internal/errorHandler.js), logger (./utils/internal/logger.js), requestContextService (./utils/internal/requestContext.js)
4
3
  // Import the final schema and types for handler logic
5
4
  // Import the BASE schema separately for registration shape
6
- import { BaseErrorCode } from '../../../types-global/errors.js';
7
5
  import { GitTagBaseSchema, gitTagLogic } from './logic.js';
8
6
  let _getWorkingDirectory;
9
7
  let _getSessionId;
@@ -0,0 +1,145 @@
1
+ /**
2
+ * MCP Authentication Middleware: Bearer Token Validation (JWT).
3
+ *
4
+ * This middleware validates JSON Web Tokens (JWT) passed via the 'Authorization' header
5
+ * using the 'Bearer' scheme (e.g., "Authorization: Bearer <your_token>").
6
+ * It verifies the token's signature and expiration using the secret key defined
7
+ * in the configuration (MCP_AUTH_SECRET_KEY).
8
+ *
9
+ * If the token is valid, the decoded payload is attached to `req.auth` for potential
10
+ * use in downstream authorization logic (e.g., checking scopes or permissions).
11
+ * If the token is missing, invalid, or expired, it sends an HTTP 401 Unauthorized response.
12
+ *
13
+ * --- Scope and Relation to MCP Authorization Spec (2025-03-26) ---
14
+ * - This middleware handles the *validation* of an already obtained Bearer token,
15
+ * as required by Section 2.6 of the MCP Auth Spec.
16
+ * - It does *NOT* implement the full OAuth 2.1 authorization flows (e.g., Authorization
17
+ * Code Grant with PKCE), token endpoints (/token), authorization endpoints (/authorize),
18
+ * metadata discovery (/.well-known/oauth-authorization-server), or dynamic client
19
+ * registration (/register) described in the specification. It assumes the client
20
+ * obtained the JWT through an external process compliant with the spec or another
21
+ * agreed-upon mechanism.
22
+ * - It correctly returns HTTP 401 errors for invalid/missing tokens as per Section 2.8.
23
+ *
24
+ * --- Implementation Details & Requirements ---
25
+ * - Requires the 'jsonwebtoken' package (`npm install jsonwebtoken @types/jsonwebtoken`).
26
+ * - The `MCP_AUTH_SECRET_KEY` environment variable MUST be set to a strong, secret value
27
+ * in production. The middleware includes a startup check for this.
28
+ * - In non-production environments, if the secret key is missing, authentication checks
29
+ * are bypassed for development convenience (a warning is logged). THIS IS INSECURE FOR PRODUCTION.
30
+ * - The structure of the JWT payload (e.g., containing user ID, scopes) depends on the
31
+ * token issuer and is not dictated by this middleware itself, but the payload is made
32
+ * available on `req.auth`.
33
+ *
34
+ * @see {@link https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-03-26/basic/authorization.mdx | MCP Authorization Specification}
35
+ */
36
+ import jwt from 'jsonwebtoken';
37
+ // Import config, environment constants, and logger
38
+ import { config, environment } from '../../../config/index.js';
39
+ import { logger } from '../../../utils/index.js';
40
+ // --- Startup Validation ---
41
+ // Validate secret key presence on module load (fail fast principle).
42
+ // This prevents the server starting insecurely in production without the key.
43
+ if (environment === 'production' && !config.security.mcpAuthSecretKey) {
44
+ logger.fatal('CRITICAL: MCP_AUTH_SECRET_KEY is not set in production environment. Authentication cannot proceed securely.');
45
+ // Throwing an error here will typically stop the Node.js process.
46
+ throw new Error('MCP_AUTH_SECRET_KEY must be set in production environment for JWT authentication.');
47
+ }
48
+ else if (!config.security.mcpAuthSecretKey) {
49
+ // Log a clear warning if running without a key in non-production environments.
50
+ logger.warning('MCP_AUTH_SECRET_KEY is not set. Authentication middleware will bypass checks (DEVELOPMENT ONLY). This is insecure for production.');
51
+ }
52
+ /**
53
+ * Express middleware function for verifying JWT Bearer token authentication.
54
+ * Checks the `Authorization` header, verifies the token, and attaches the payload to `req.auth`.
55
+ *
56
+ * @param {Request} req - Express request object.
57
+ * @param {Response} res - Express response object.
58
+ * @param {NextFunction} next - Express next middleware function.
59
+ */
60
+ export function mcpAuthMiddleware(req, res, next) {
61
+ // Establish context for logging associated with this middleware execution.
62
+ const context = { operation: 'mcpAuthMiddleware', method: req.method, path: req.path };
63
+ logger.debug('Running MCP Authentication Middleware (Bearer Token Validation)...', context);
64
+ // --- Development Mode Bypass ---
65
+ // If the secret key is missing (and not in production), bypass authentication.
66
+ if (!config.security.mcpAuthSecretKey) {
67
+ // Double-check environment for safety, although the startup check should prevent this in prod.
68
+ if (environment !== 'production') {
69
+ logger.warning('Bypassing JWT authentication: MCP_AUTH_SECRET_KEY is not set (DEVELOPMENT ONLY).', context);
70
+ // Attach a dummy auth object to indicate bypass for potential downstream checks.
71
+ req.auth = { devMode: true, warning: 'Auth bypassed due to missing secret key' };
72
+ return next(); // Proceed without authentication.
73
+ }
74
+ else {
75
+ // Defensive coding: Should be caught by startup check, but handle anyway.
76
+ logger.error('FATAL: MCP_AUTH_SECRET_KEY is missing in production. Cannot bypass auth.', context);
77
+ // Send a server error response as this indicates a critical configuration issue.
78
+ res.status(500).json({ error: 'Server configuration error: Authentication key missing.' });
79
+ return; // Halt processing.
80
+ }
81
+ }
82
+ // --- Standard JWT Bearer Token Verification ---
83
+ const authHeader = req.headers.authorization;
84
+ logger.debug(`Authorization header present: ${!!authHeader}`, context);
85
+ // Check for the presence and correct format ('Bearer <token>') of the Authorization header.
86
+ if (!authHeader || !authHeader.startsWith('Bearer ')) {
87
+ logger.warning('Authentication failed: Missing or malformed Authorization header (Bearer scheme required).', context);
88
+ // Respond with 401 Unauthorized as per RFC 6750 (Bearer Token Usage).
89
+ res.status(401).json({ error: 'Unauthorized: Missing or invalid authentication token format.' });
90
+ return; // Halt processing.
91
+ }
92
+ // Extract the token part from the "Bearer <token>" string.
93
+ const token = authHeader.split(' ')[1];
94
+ // Avoid logging the token itself for security reasons.
95
+ logger.debug('Extracted token from Bearer header.', context);
96
+ // Check if a token was actually present after the split.
97
+ if (!token) {
98
+ logger.warning('Authentication failed: Token missing after Bearer split (Malformed header).', context);
99
+ res.status(401).json({ error: 'Unauthorized: Malformed authentication token.' });
100
+ return; // Halt processing.
101
+ }
102
+ try {
103
+ // Verify the token's signature and expiration using the configured secret key.
104
+ // `jwt.verify` throws errors for invalid signature, expiration, etc.
105
+ const decoded = jwt.verify(token, config.security.mcpAuthSecretKey);
106
+ // Avoid logging the decoded payload directly unless necessary for specific debugging,
107
+ // as it might contain sensitive information.
108
+ logger.debug('JWT verified successfully.', { ...context });
109
+ // Attach the decoded payload (which can be an object or string based on JWT content)
110
+ // to the request object (`req.auth`) for use in subsequent middleware or route handlers
111
+ // (e.g., for fine-grained authorization checks based on payload claims like user ID or scopes).
112
+ req.auth = decoded;
113
+ // Authentication successful, proceed to the next middleware or the main route handler.
114
+ next();
115
+ }
116
+ catch (error) {
117
+ // Handle errors thrown by `jwt.verify`.
118
+ let errorMessage = 'Invalid token'; // Default error message.
119
+ if (error instanceof jwt.TokenExpiredError) {
120
+ // Specific error for expired tokens.
121
+ errorMessage = 'Token expired';
122
+ // After instanceof check, 'error' is typed as TokenExpiredError
123
+ logger.warning('Authentication failed: Token expired.', { ...context, expiredAt: error.expiredAt }); // Log specific details here
124
+ }
125
+ else if (error instanceof jwt.JsonWebTokenError) {
126
+ // General JWT errors (e.g., invalid signature, malformed token).
127
+ // After instanceof check, 'error' is typed as JsonWebTokenError
128
+ errorMessage = `Invalid token: ${error.message}`; // Include specific JWT error message
129
+ logger.warning(`Authentication failed: ${errorMessage}`, { ...context }); // Log specific details here
130
+ }
131
+ else if (error instanceof Error) {
132
+ // Handle other standard JavaScript errors
133
+ errorMessage = `Verification error: ${error.message}`;
134
+ logger.error('Authentication failed: Unexpected error during token verification.', { ...context, error: error.message }); // Log specific details here
135
+ }
136
+ else {
137
+ // Handle non-Error exceptions
138
+ errorMessage = 'Unknown verification error';
139
+ logger.error('Authentication failed: Unexpected non-error exception during token verification.', { ...context, error });
140
+ }
141
+ // Respond with 401 Unauthorized for any token validation failure.
142
+ res.status(401).json({ error: `Unauthorized: ${errorMessage}.` });
143
+ // Do not call next() - halt processing for this request.
144
+ }
145
+ }