@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
@@ -0,0 +1,254 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { fileURLToPath } from 'url';
4
+ import winston from 'winston';
5
+ import { config } from '../../config/index.js';
6
+ // Define the numeric severity for comparison (lower is more severe)
7
+ const mcpLevelSeverity = {
8
+ emerg: 0, alert: 1, crit: 2, error: 3, warning: 4, notice: 5, info: 6, debug: 7
9
+ };
10
+ // Map MCP levels to Winston's core levels for file logging
11
+ const mcpToWinstonLevel = {
12
+ debug: 'debug',
13
+ info: 'info',
14
+ notice: 'info', // Map notice to info for file logging
15
+ warning: 'warn',
16
+ error: 'error',
17
+ crit: 'error', // Map critical levels to error for file logging
18
+ alert: 'error',
19
+ emerg: 'error',
20
+ };
21
+ // Resolve __dirname for ESM
22
+ const __filename = fileURLToPath(import.meta.url);
23
+ const __dirname = path.dirname(__filename);
24
+ // Calculate project root robustly (works from src/ or dist/)
25
+ const isRunningFromDist = __dirname.includes(path.sep + 'dist' + path.sep);
26
+ const levelsToGoUp = isRunningFromDist ? 3 : 2;
27
+ const pathSegments = Array(levelsToGoUp).fill('..');
28
+ const projectRoot = path.resolve(__dirname, ...pathSegments);
29
+ const logsDir = path.join(projectRoot, 'logs');
30
+ // Security: ensure logsDir is within projectRoot
31
+ const resolvedLogsDir = path.resolve(logsDir);
32
+ const isLogsDirSafe = resolvedLogsDir === projectRoot || resolvedLogsDir.startsWith(projectRoot + path.sep);
33
+ if (!isLogsDirSafe) {
34
+ // Use console.error here as logger might not be initialized or safe
35
+ console.error(`FATAL: logs directory "${resolvedLogsDir}" is outside project root "${projectRoot}". File logging disabled.`);
36
+ }
37
+ /**
38
+ * Singleton Logger wrapping Winston, adapted for MCP.
39
+ * Logs to files and optionally sends MCP notifications/message.
40
+ */
41
+ class Logger {
42
+ static instance;
43
+ winstonLogger;
44
+ initialized = false;
45
+ mcpNotificationSender;
46
+ currentMcpLevel = 'info'; // Default MCP level
47
+ currentWinstonLevel = 'info'; // Default Winston level
48
+ constructor() { }
49
+ /**
50
+ * Initialize Winston logger for file transport. Must be called once at app start.
51
+ * Console transport is added conditionally.
52
+ * @param level Initial minimum level to log ('info' default).
53
+ */
54
+ async initialize(level = 'info') {
55
+ if (this.initialized) {
56
+ console.warn('Logger already initialized.');
57
+ return;
58
+ }
59
+ this.currentMcpLevel = level;
60
+ this.currentWinstonLevel = mcpToWinstonLevel[level];
61
+ // Ensure logs directory exists
62
+ if (isLogsDirSafe) {
63
+ try {
64
+ if (!fs.existsSync(resolvedLogsDir)) {
65
+ fs.mkdirSync(resolvedLogsDir, { recursive: true });
66
+ console.log(`Created logs directory: ${resolvedLogsDir}`);
67
+ }
68
+ }
69
+ catch (err) {
70
+ console.error(`Error creating logs directory at ${resolvedLogsDir}: ${err.message}. File logging disabled.`);
71
+ }
72
+ }
73
+ // Common format for files
74
+ const fileFormat = winston.format.combine(winston.format.timestamp(), winston.format.errors({ stack: true }), winston.format.json());
75
+ const transports = [];
76
+ // Add file transports only if the directory is safe
77
+ if (isLogsDirSafe) {
78
+ transports.push(new winston.transports.File({ filename: path.join(resolvedLogsDir, 'error.log'), level: 'error', format: fileFormat }), new winston.transports.File({ filename: path.join(resolvedLogsDir, 'warn.log'), level: 'warn', format: fileFormat }), new winston.transports.File({ filename: path.join(resolvedLogsDir, 'info.log'), level: 'info', format: fileFormat }), new winston.transports.File({ filename: path.join(resolvedLogsDir, 'debug.log'), level: 'debug', format: fileFormat }), new winston.transports.File({ filename: path.join(resolvedLogsDir, 'combined.log'), format: fileFormat }));
79
+ }
80
+ else {
81
+ console.warn("File logging disabled due to unsafe logs directory path.");
82
+ }
83
+ // Conditionally add Console transport only if:
84
+ // 1. MCP level is 'debug'
85
+ // 2. stdout is a TTY (interactive terminal, not piped)
86
+ if (this.currentMcpLevel === 'debug' && process.stdout.isTTY) {
87
+ const consoleFormat = winston.format.combine(winston.format.colorize(), winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), winston.format.printf(({ timestamp, level, message, ...meta }) => {
88
+ let metaString = '';
89
+ const metaCopy = { ...meta };
90
+ if (metaCopy.error && typeof metaCopy.error === 'object') {
91
+ const errorObj = metaCopy.error;
92
+ if (errorObj.message)
93
+ metaString += `\n Error: ${errorObj.message}`;
94
+ if (errorObj.stack)
95
+ metaString += `\n Stack: ${String(errorObj.stack).split('\n').map((l) => ` ${l}`).join('\n')}`;
96
+ delete metaCopy.error;
97
+ }
98
+ if (Object.keys(metaCopy).length > 0) {
99
+ try {
100
+ const remainingMetaJson = JSON.stringify(metaCopy, null, 2);
101
+ if (remainingMetaJson !== '{}')
102
+ metaString += `\n Meta: ${remainingMetaJson}`;
103
+ }
104
+ catch (stringifyError) {
105
+ metaString += `\n Meta: [Error stringifying metadata: ${stringifyError.message}]`;
106
+ }
107
+ }
108
+ return `${timestamp} ${level}: ${message}${metaString}`;
109
+ }));
110
+ transports.push(new winston.transports.Console({
111
+ level: 'debug',
112
+ format: consoleFormat,
113
+ }));
114
+ console.log(`Console logging enabled at level: debug (stdout is TTY)`);
115
+ }
116
+ else if (this.currentMcpLevel === 'debug' && !process.stdout.isTTY) {
117
+ console.log(`Console logging skipped: Level is debug, but stdout is not a TTY (likely stdio transport).`);
118
+ }
119
+ // Create logger with the initial Winston level and configured transports
120
+ this.winstonLogger = winston.createLogger({
121
+ level: this.currentWinstonLevel,
122
+ transports,
123
+ exitOnError: false
124
+ });
125
+ this.initialized = true;
126
+ await Promise.resolve(); // Yield to event loop
127
+ this.info(`Logger initialized. File logging level: ${this.currentWinstonLevel}. MCP logging level: ${this.currentMcpLevel}. Console logging: ${process.stdout.isTTY && this.currentMcpLevel === 'debug' ? 'enabled' : 'disabled'}`);
128
+ }
129
+ /**
130
+ * Sets the function used to send MCP 'notifications/message'.
131
+ */
132
+ setMcpNotificationSender(sender) {
133
+ this.mcpNotificationSender = sender;
134
+ const status = sender ? 'enabled' : 'disabled';
135
+ this.info(`MCP notification sending ${status}.`);
136
+ }
137
+ /**
138
+ * Dynamically sets the minimum logging level.
139
+ */
140
+ setLevel(newLevel) {
141
+ if (!this.ensureInitialized()) {
142
+ console.error("Cannot set level: Logger not initialized.");
143
+ return;
144
+ }
145
+ if (!(newLevel in mcpLevelSeverity)) {
146
+ this.warning(`Invalid MCP log level provided: ${newLevel}. Level not changed.`);
147
+ return;
148
+ }
149
+ const oldLevel = this.currentMcpLevel;
150
+ this.currentMcpLevel = newLevel;
151
+ this.currentWinstonLevel = mcpToWinstonLevel[newLevel];
152
+ this.winstonLogger.level = this.currentWinstonLevel;
153
+ // Add or remove console transport based on the new level and TTY status
154
+ const consoleTransport = this.winstonLogger.transports.find(t => t instanceof winston.transports.Console);
155
+ const shouldHaveConsole = newLevel === 'debug' && process.stdout.isTTY;
156
+ if (shouldHaveConsole && !consoleTransport) {
157
+ // Add console transport
158
+ const consoleFormat = winston.format.combine( /* ... same format as in initialize ... */); // TODO: Extract format to avoid duplication
159
+ this.winstonLogger.add(new winston.transports.Console({ level: 'debug', format: consoleFormat }));
160
+ this.info('Console logging dynamically enabled.');
161
+ }
162
+ else if (!shouldHaveConsole && consoleTransport) {
163
+ // Remove console transport
164
+ this.winstonLogger.remove(consoleTransport);
165
+ this.info('Console logging dynamically disabled.');
166
+ }
167
+ if (oldLevel !== newLevel) {
168
+ this.info(`Log level changed. File logging level: ${this.currentWinstonLevel}. MCP logging level: ${this.currentMcpLevel}. Console logging: ${shouldHaveConsole ? 'enabled' : 'disabled'}`);
169
+ }
170
+ }
171
+ /** Get singleton instance. */
172
+ static getInstance() {
173
+ if (!Logger.instance) {
174
+ Logger.instance = new Logger();
175
+ }
176
+ return Logger.instance;
177
+ }
178
+ /** Ensures the logger has been initialized. */
179
+ ensureInitialized() {
180
+ if (!this.initialized || !this.winstonLogger) {
181
+ console.warn('Logger not initialized; message dropped.');
182
+ return false;
183
+ }
184
+ return true;
185
+ }
186
+ /** Centralized log processing */
187
+ log(level, msg, context, error) {
188
+ if (!this.ensureInitialized())
189
+ return;
190
+ if (mcpLevelSeverity[level] > mcpLevelSeverity[this.currentMcpLevel]) {
191
+ return;
192
+ }
193
+ const logData = { ...context };
194
+ const winstonLevel = mcpToWinstonLevel[level];
195
+ if (error) {
196
+ this.winstonLogger.log(winstonLevel, msg, { ...logData, error: error });
197
+ }
198
+ else {
199
+ this.winstonLogger.log(winstonLevel, msg, logData);
200
+ }
201
+ if (this.mcpNotificationSender) {
202
+ const mcpDataPayload = { message: msg };
203
+ if (context)
204
+ mcpDataPayload.context = context;
205
+ if (error) {
206
+ mcpDataPayload.error = { message: error.message };
207
+ if (this.currentMcpLevel === 'debug' && error.stack) {
208
+ mcpDataPayload.error.stack = error.stack.substring(0, 500);
209
+ }
210
+ }
211
+ try {
212
+ this.mcpNotificationSender(level, mcpDataPayload, config.mcpServerName);
213
+ }
214
+ catch (sendError) {
215
+ this.winstonLogger.error("Failed to send MCP log notification", {
216
+ originalLevel: level,
217
+ originalMessage: msg,
218
+ sendError: sendError instanceof Error ? sendError.message : String(sendError),
219
+ mcpPayload: mcpDataPayload
220
+ });
221
+ }
222
+ }
223
+ }
224
+ // --- Public Logging Methods ---
225
+ debug(msg, context) { this.log('debug', msg, context); }
226
+ info(msg, context) { this.log('info', msg, context); }
227
+ notice(msg, context) { this.log('notice', msg, context); }
228
+ warning(msg, context) { this.log('warning', msg, context); }
229
+ error(msg, err, context) {
230
+ const errorObj = err instanceof Error ? err : undefined;
231
+ const combinedContext = err instanceof Error ? context : { ...(err || {}), ...(context || {}) };
232
+ this.log('error', msg, combinedContext, errorObj);
233
+ }
234
+ crit(msg, err, context) {
235
+ const errorObj = err instanceof Error ? err : undefined;
236
+ const combinedContext = err instanceof Error ? context : { ...(err || {}), ...(context || {}) };
237
+ this.log('crit', msg, combinedContext, errorObj);
238
+ }
239
+ alert(msg, err, context) {
240
+ const errorObj = err instanceof Error ? err : undefined;
241
+ const combinedContext = err instanceof Error ? context : { ...(err || {}), ...(context || {}) };
242
+ this.log('alert', msg, combinedContext, errorObj);
243
+ }
244
+ emerg(msg, err, context) {
245
+ const errorObj = err instanceof Error ? err : undefined;
246
+ const combinedContext = err instanceof Error ? context : { ...(err || {}), ...(context || {}) };
247
+ this.log('emerg', msg, combinedContext, errorObj);
248
+ }
249
+ fatal(msg, context, error) {
250
+ this.log('emerg', msg, context, error);
251
+ }
252
+ }
253
+ // Export singleton instance
254
+ export const logger = Logger.getInstance();
@@ -1,5 +1,6 @@
1
1
  import { logger } from './logger.js';
2
- import { generateUUID } from './idGenerator.js'; // Import generateUUID
2
+ // Import utils from the main barrel file (generateUUID from ../security/idGenerator.js)
3
+ import { generateUUID } from '../index.js';
3
4
  // Direct instance for request context utilities
4
5
  const requestContextServiceInstance = {
5
6
  config: {},
@@ -39,8 +40,6 @@ const requestContextServiceInstance = {
39
40
  },
40
41
  // generateSecureRandomString function removed as it was unused and redundant
41
42
  };
42
- // Initialize logger message
43
- logger.debug('RequestContext service initialized');
44
43
  // Export the instance directly
45
44
  export const requestContextService = requestContextServiceInstance;
46
45
  // Removed delegate functions and default export for simplicity.
@@ -0,0 +1 @@
1
+ export * from './tokenCounter.js';
@@ -1,7 +1,7 @@
1
1
  import { encoding_for_model } from 'tiktoken';
2
- import { BaseErrorCode } from '../types-global/errors.js'; // Import BaseErrorCode and McpError
3
- import { ErrorHandler } from './errorHandler.js'; // Import ErrorHandler
4
- import { logger } from './logger.js';
2
+ import { BaseErrorCode } from '../../types-global/errors.js';
3
+ // Import utils from the main barrel file (ErrorHandler, logger, RequestContext from ../internal/*)
4
+ import { ErrorHandler, logger } from '../index.js';
5
5
  // Define the model used specifically for token counting
6
6
  const TOKENIZATION_MODEL = 'gpt-4o'; // Note this is strictly for token counting, not the model used for inference
7
7
  /**
@@ -0,0 +1,62 @@
1
+ import * as chrono from 'chrono-node';
2
+ // Import utils from the main barrel file (logger, ErrorHandler, RequestContext from ../internal/*)
3
+ import { logger, ErrorHandler } from '../index.js';
4
+ import { BaseErrorCode } from '../../types-global/errors.js'; // Corrected path
5
+ /**
6
+ * Parses a natural language date string into a Date object.
7
+ *
8
+ * @param text The natural language date string (e.g., "tomorrow", "in 5 days", "2024-01-15").
9
+ * @param context The request context for logging and error tracking.
10
+ * @param refDate Optional reference date for parsing relative dates. Defaults to now.
11
+ * @returns A Date object representing the parsed date, or null if parsing fails.
12
+ * @throws McpError if parsing fails unexpectedly.
13
+ */
14
+ async function parseDateString(text, context, refDate) {
15
+ const operation = 'parseDateString';
16
+ const logContext = { ...context, operation, inputText: text, refDate };
17
+ logger.debug(`Attempting to parse date string: "${text}"`, logContext);
18
+ return await ErrorHandler.tryCatch(async () => {
19
+ const parsedDate = chrono.parseDate(text, refDate, { forwardDate: true });
20
+ if (parsedDate) {
21
+ logger.debug(`Successfully parsed "${text}" to ${parsedDate.toISOString()}`, logContext);
22
+ return parsedDate;
23
+ }
24
+ else {
25
+ logger.warning(`Failed to parse date string: "${text}"`, logContext);
26
+ return null;
27
+ }
28
+ }, {
29
+ operation,
30
+ context: logContext,
31
+ input: { text, refDate },
32
+ errorCode: BaseErrorCode.PARSING_ERROR,
33
+ });
34
+ }
35
+ /**
36
+ * Parses a natural language date string and returns detailed parsing results.
37
+ *
38
+ * @param text The natural language date string.
39
+ * @param context The request context for logging and error tracking.
40
+ * @param refDate Optional reference date for parsing relative dates. Defaults to now.
41
+ * @returns An array of chrono.ParsedResult objects, or an empty array if parsing fails.
42
+ * @throws McpError if parsing fails unexpectedly.
43
+ */
44
+ async function parseDateStringDetailed(text, context, refDate) {
45
+ const operation = 'parseDateStringDetailed';
46
+ const logContext = { ...context, operation, inputText: text, refDate };
47
+ logger.debug(`Attempting detailed parse of date string: "${text}"`, logContext);
48
+ return await ErrorHandler.tryCatch(async () => {
49
+ const results = chrono.parse(text, refDate, { forwardDate: true });
50
+ logger.debug(`Detailed parse of "${text}" resulted in ${results.length} result(s)`, logContext);
51
+ return results;
52
+ }, {
53
+ operation,
54
+ context: logContext,
55
+ input: { text, refDate },
56
+ errorCode: BaseErrorCode.PARSING_ERROR,
57
+ });
58
+ }
59
+ export const dateParser = {
60
+ parse: parseDateStringDetailed,
61
+ parseDate: parseDateString,
62
+ };
@@ -0,0 +1,2 @@
1
+ export * from './jsonParser.js';
2
+ export * from './dateParser.js';
@@ -1,6 +1,7 @@
1
1
  import { parse as parsePartialJson, Allow as PartialJsonAllow } from 'partial-json';
2
- import { BaseErrorCode, McpError } from '../types-global/errors.js';
3
- import { logger } from './logger.js'; // Import logger
2
+ import { BaseErrorCode, McpError } from '../../types-global/errors.js';
3
+ // Import utils from the main barrel file (logger, RequestContext from ../internal/*)
4
+ import { logger } from '../index.js';
4
5
  /**
5
6
  * Enum mirroring partial-json's Allow constants for specifying
6
7
  * what types of partial JSON structures are permissible during parsing.
@@ -1,6 +1,5 @@
1
- import { randomBytes } from 'crypto';
2
- import { BaseErrorCode, McpError } from '../types-global/errors.js';
3
- import { logger } from './logger.js';
1
+ import { randomBytes, randomUUID as cryptoRandomUUID } from 'crypto'; // Import cryptoRandomUUID
2
+ import { BaseErrorCode, McpError } from '../../types-global/errors.js'; // Corrected path
4
3
  /**
5
4
  * Generic ID Generator class for creating and managing unique identifiers
6
5
  */
@@ -34,7 +33,7 @@ export class IdGenerator {
34
33
  acc[prefix.toLowerCase()] = type;
35
34
  return acc;
36
35
  }, {});
37
- logger.debug('Entity prefixes updated', { entityPrefixes: this.entityPrefixes });
36
+ // Removed logger call from setEntityPrefixes to prevent logging before initialization
38
37
  }
39
38
  /**
40
39
  * Get all registered entity prefixes
@@ -144,5 +143,5 @@ export class IdGenerator {
144
143
  export const idGenerator = new IdGenerator();
145
144
  // For standalone use as a UUID generator
146
145
  export const generateUUID = () => {
147
- return crypto.randomUUID();
146
+ return cryptoRandomUUID(); // Use imported cryptoRandomUUID
148
147
  };
@@ -0,0 +1,3 @@
1
+ export * from './sanitization.js';
2
+ export * from './rateLimiter.js';
3
+ export * from './idGenerator.js';
@@ -1,5 +1,7 @@
1
- import { BaseErrorCode, McpError } from '../types-global/errors.js';
2
- import { logger } from './logger.js';
1
+ import { BaseErrorCode, McpError } from '../../types-global/errors.js';
2
+ // Import config and utils
3
+ import { environment } from '../../config/index.js'; // Import environment from config
4
+ import { logger } from '../index.js';
3
5
  /**
4
6
  * Generic rate limiter that can be used across the application
5
7
  */
@@ -26,12 +28,7 @@ export class RateLimiter {
26
28
  this.config = { ...RateLimiter.DEFAULT_CONFIG, ...config };
27
29
  this.limits = new Map();
28
30
  this.startCleanupTimer();
29
- // Log initialization
30
- logger.debug('RateLimiter initialized', {
31
- windowMs: this.config.windowMs,
32
- maxRequests: this.config.maxRequests,
33
- cleanupInterval: this.config.cleanupInterval
34
- });
31
+ // Removed logger call from constructor to prevent logging before initialization
35
32
  }
36
33
  /**
37
34
  * Start the cleanup timer to periodically remove expired entries
@@ -102,8 +99,8 @@ export class RateLimiter {
102
99
  * @throws {McpError} If rate limit is exceeded
103
100
  */
104
101
  check(key, context) {
105
- // Skip in development if configured
106
- if (this.config.skipInDevelopment && process.env.NODE_ENV === 'development') {
102
+ // Skip in development if configured, using the validated environment from config
103
+ if (this.config.skipInDevelopment && environment === 'development') {
107
104
  return;
108
105
  }
109
106
  // Generate key using custom generator if provided
@@ -1,8 +1,9 @@
1
1
  import path from 'path';
2
2
  import sanitizeHtml from 'sanitize-html';
3
3
  import validator from 'validator';
4
- import { BaseErrorCode, McpError } from '../types-global/errors.js';
5
- import { logger } from './logger.js';
4
+ import { BaseErrorCode, McpError } from '../../types-global/errors.js';
5
+ // Import utils from the main barrel file (logger from ../internal/logger.js)
6
+ import { logger } from '../index.js';
6
7
  /**
7
8
  * Sanitization class for handling various input sanitization tasks
8
9
  */
@@ -31,7 +32,7 @@ export class Sanitization {
31
32
  * Private constructor to enforce singleton pattern
32
33
  */
33
34
  constructor() {
34
- logger.debug('Sanitization service initialized with modern libraries');
35
+ // Removed logger call from constructor to prevent logging before initialization
35
36
  }
36
37
  /**
37
38
  * Get the singleton Sanitization instance
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cyanheads/git-mcp-server",
3
- "version": "2.0.1",
3
+ "version": "2.0.3",
4
4
  "description": "An MCP (Model Context Protocol) server providing tools to interact with Git repositories. Enables LLMs and AI agents to perform Git operations like clone, commit, push, pull, branch, diff, log, status, and more via the MCP standard.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -17,42 +17,46 @@
17
17
  "node": ">=18.0.0"
18
18
  },
19
19
  "bin": {
20
- "git-mcp-server": "./build/index.js"
20
+ "git-mcp-server": "./dist/index.js"
21
21
  },
22
22
  "files": [
23
- "build"
23
+ "dist"
24
24
  ],
25
25
  "scripts": {
26
- "build": "tsc && node -e \"require('fs').chmodSync('build/index.js', '755')\"",
27
- "prepare": "npm run build",
28
- "watch": "tsc --watch",
29
- "inspector": "npx @modelcontextprotocol/inspector build/index.js",
30
- "clean": "ts-node scripts/clean.ts",
31
- "tree": "ts-node scripts/tree.ts",
32
- "rebuild": "npm run clean && npm run build"
26
+ "build": "tsc && node --loader ts-node/esm scripts/make-executable.ts dist/index.js",
27
+ "start": "node dist/index.js",
28
+ "start:stdio": "MCP_LOG_LEVEL=debug MCP_TRANSPORT_TYPE=stdio node dist/index.js",
29
+ "start:http": "MCP_LOG_LEVEL=debug MCP_TRANSPORT_TYPE=http node dist/index.js",
30
+ "rebuild": "ts-node --esm scripts/clean.ts && npm run build",
31
+ "tree": "ts-node --esm scripts/tree.ts",
32
+ "inspector": "npx @modelcontextprotocol/inspector dist/index.js",
33
+ "clean": "ts-node --esm scripts/clean.ts"
33
34
  },
34
35
  "publishConfig": {
35
36
  "access": "public"
36
37
  },
37
38
  "dependencies": {
38
- "@modelcontextprotocol/sdk": "^1.10.2",
39
- "@types/node": "^22.15.3",
39
+ "@modelcontextprotocol/sdk": "^1.11.0",
40
+ "@types/jsonwebtoken": "^9.0.9",
41
+ "@types/node": "^22.15.4",
40
42
  "@types/sanitize-html": "^2.15.0",
41
- "@types/validator": "13.15.0",
43
+ "@types/validator": "^13.15.0",
44
+ "chrono-node": "^2.8.0",
42
45
  "dotenv": "^16.5.0",
43
46
  "express": "^5.1.0",
44
47
  "ignore": "^7.0.4",
45
- "openai": "^4.96.2",
48
+ "jsonwebtoken": "^9.0.2",
49
+ "openai": "^4.97.0",
46
50
  "partial-json": "^0.1.7",
47
51
  "sanitize-html": "^2.16.0",
48
52
  "tiktoken": "^1.0.21",
49
53
  "ts-node": "^10.9.2",
50
54
  "typescript": "^5.8.3",
51
- "validator": "13.15.0",
55
+ "validator": "^13.15.0",
52
56
  "winston": "^3.17.0",
53
57
  "winston-daily-rotate-file": "^5.0.0",
54
58
  "yargs": "^17.7.2",
55
- "zod": "^3.24.3"
59
+ "zod": "^3.24.4"
56
60
  },
57
61
  "keywords": [
58
62
  "typescript",