@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.
- package/README.md +55 -89
- package/{build → dist}/config/index.js +16 -18
- package/{build → dist}/index.js +80 -30
- package/dist/mcp-server/server.js +296 -0
- package/{build → dist}/mcp-server/tools/gitAdd/logic.js +9 -6
- package/{build → dist}/mcp-server/tools/gitAdd/registration.js +7 -4
- package/{build → dist}/mcp-server/tools/gitBranch/logic.js +23 -12
- package/{build → dist}/mcp-server/tools/gitBranch/registration.js +8 -5
- package/{build → dist}/mcp-server/tools/gitCheckout/logic.js +92 -44
- package/{build → dist}/mcp-server/tools/gitCheckout/registration.js +8 -5
- package/{build → dist}/mcp-server/tools/gitCherryPick/logic.js +10 -7
- package/{build → dist}/mcp-server/tools/gitCherryPick/registration.js +8 -5
- package/{build → dist}/mcp-server/tools/gitClean/logic.js +9 -6
- package/{build → dist}/mcp-server/tools/gitClean/registration.js +8 -5
- package/{build → dist}/mcp-server/tools/gitClearWorkingDir/logic.js +3 -2
- package/{build → dist}/mcp-server/tools/gitClearWorkingDir/registration.js +7 -4
- package/{build → dist}/mcp-server/tools/gitClone/logic.js +8 -5
- package/{build → dist}/mcp-server/tools/gitClone/registration.js +7 -4
- package/dist/mcp-server/tools/gitCommit/logic.js +207 -0
- package/{build → dist}/mcp-server/tools/gitCommit/registration.js +22 -15
- package/{build → dist}/mcp-server/tools/gitDiff/logic.js +9 -6
- package/{build → dist}/mcp-server/tools/gitDiff/registration.js +8 -5
- package/{build → dist}/mcp-server/tools/gitFetch/logic.js +10 -7
- package/{build → dist}/mcp-server/tools/gitFetch/registration.js +8 -5
- package/{build → dist}/mcp-server/tools/gitInit/index.js +2 -2
- package/{build → dist}/mcp-server/tools/gitInit/logic.js +9 -6
- package/dist/mcp-server/tools/gitInit/registration.js +98 -0
- package/{build → dist}/mcp-server/tools/gitLog/logic.js +53 -16
- package/{build → dist}/mcp-server/tools/gitLog/registration.js +8 -5
- package/{build → dist}/mcp-server/tools/gitMerge/logic.js +9 -6
- package/{build → dist}/mcp-server/tools/gitMerge/registration.js +8 -5
- package/{build → dist}/mcp-server/tools/gitPull/logic.js +11 -8
- package/{build → dist}/mcp-server/tools/gitPull/registration.js +7 -4
- package/{build → dist}/mcp-server/tools/gitPush/logic.js +12 -9
- package/{build → dist}/mcp-server/tools/gitPush/registration.js +7 -4
- package/{build → dist}/mcp-server/tools/gitRebase/logic.js +9 -6
- package/{build → dist}/mcp-server/tools/gitRebase/registration.js +8 -5
- package/{build → dist}/mcp-server/tools/gitRemote/logic.js +4 -5
- package/{build → dist}/mcp-server/tools/gitRemote/registration.js +2 -4
- package/{build → dist}/mcp-server/tools/gitReset/logic.js +5 -6
- package/{build → dist}/mcp-server/tools/gitReset/registration.js +2 -4
- package/{build → dist}/mcp-server/tools/gitSetWorkingDir/logic.js +5 -6
- package/{build → dist}/mcp-server/tools/gitSetWorkingDir/registration.js +22 -13
- package/{build → dist}/mcp-server/tools/gitShow/logic.js +5 -6
- package/{build → dist}/mcp-server/tools/gitShow/registration.js +3 -5
- package/{build → dist}/mcp-server/tools/gitStash/logic.js +5 -6
- package/{build → dist}/mcp-server/tools/gitStash/registration.js +3 -5
- package/{build → dist}/mcp-server/tools/gitStatus/logic.js +5 -6
- package/{build → dist}/mcp-server/tools/gitStatus/registration.js +2 -4
- package/{build → dist}/mcp-server/tools/gitTag/logic.js +3 -4
- package/{build → dist}/mcp-server/tools/gitTag/registration.js +2 -4
- package/dist/mcp-server/transports/authentication/authMiddleware.js +145 -0
- package/dist/mcp-server/transports/httpTransport.js +432 -0
- package/dist/mcp-server/transports/stdioTransport.js +87 -0
- package/{build → dist}/types-global/errors.js +2 -2
- package/dist/utils/index.js +12 -0
- package/{build/utils → dist/utils/internal}/errorHandler.js +18 -8
- package/dist/utils/internal/index.js +3 -0
- package/dist/utils/internal/logger.js +254 -0
- package/{build/utils → dist/utils/internal}/requestContext.js +2 -3
- package/dist/utils/metrics/index.js +1 -0
- package/{build/utils → dist/utils/metrics}/tokenCounter.js +3 -3
- package/dist/utils/parsing/dateParser.js +62 -0
- package/dist/utils/parsing/index.js +2 -0
- package/{build/utils → dist/utils/parsing}/jsonParser.js +3 -2
- package/{build/utils → dist/utils/security}/idGenerator.js +4 -5
- package/dist/utils/security/index.js +3 -0
- package/{build/utils → dist/utils/security}/rateLimiter.js +7 -10
- package/{build/utils → dist/utils/security}/sanitization.js +4 -3
- package/package.json +20 -16
- package/build/mcp-server/server.js +0 -572
- package/build/mcp-server/tools/gitCommit/logic.js +0 -129
- package/build/mcp-server/tools/gitInit/registration.js +0 -44
- package/build/types-global/mcp.js +0 -59
- package/build/types-global/tool.js +0 -1
- package/build/utils/index.js +0 -11
- package/build/utils/logger.js +0 -266
- /package/{build → dist}/mcp-server/tools/gitAdd/index.js +0 -0
- /package/{build → dist}/mcp-server/tools/gitBranch/index.js +0 -0
- /package/{build → dist}/mcp-server/tools/gitCheckout/index.js +0 -0
- /package/{build → dist}/mcp-server/tools/gitCherryPick/index.js +0 -0
- /package/{build → dist}/mcp-server/tools/gitClean/index.js +0 -0
- /package/{build → dist}/mcp-server/tools/gitClearWorkingDir/index.js +0 -0
- /package/{build → dist}/mcp-server/tools/gitClone/index.js +0 -0
- /package/{build → dist}/mcp-server/tools/gitCommit/index.js +0 -0
- /package/{build → dist}/mcp-server/tools/gitDiff/index.js +0 -0
- /package/{build → dist}/mcp-server/tools/gitFetch/index.js +0 -0
- /package/{build → dist}/mcp-server/tools/gitLog/index.js +0 -0
- /package/{build → dist}/mcp-server/tools/gitMerge/index.js +0 -0
- /package/{build → dist}/mcp-server/tools/gitPull/index.js +0 -0
- /package/{build → dist}/mcp-server/tools/gitPush/index.js +0 -0
- /package/{build → dist}/mcp-server/tools/gitRebase/index.js +0 -0
- /package/{build → dist}/mcp-server/tools/gitRemote/index.js +0 -0
- /package/{build → dist}/mcp-server/tools/gitReset/index.js +0 -0
- /package/{build → dist}/mcp-server/tools/gitSetWorkingDir/index.js +0 -0
- /package/{build → dist}/mcp-server/tools/gitShow/index.js +0 -0
- /package/{build → dist}/mcp-server/tools/gitStash/index.js +0 -0
- /package/{build → dist}/mcp-server/tools/gitStatus/index.js +0 -0
- /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
|
-
|
|
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 '
|
|
3
|
-
|
|
4
|
-
import { logger } from '
|
|
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
|
+
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { parse as parsePartialJson, Allow as PartialJsonAllow } from 'partial-json';
|
|
2
|
-
import { BaseErrorCode, McpError } from '
|
|
3
|
-
|
|
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 '
|
|
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
|
|
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
|
|
146
|
+
return cryptoRandomUUID(); // Use imported cryptoRandomUUID
|
|
148
147
|
};
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import { BaseErrorCode, McpError } from '
|
|
2
|
-
|
|
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
|
-
//
|
|
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 &&
|
|
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 '
|
|
5
|
-
|
|
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
|
|
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.
|
|
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": "./
|
|
20
|
+
"git-mcp-server": "./dist/index.js"
|
|
21
21
|
},
|
|
22
22
|
"files": [
|
|
23
|
-
"
|
|
23
|
+
"dist"
|
|
24
24
|
],
|
|
25
25
|
"scripts": {
|
|
26
|
-
"build": "tsc && node -
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"tree": "ts-node scripts/tree.ts",
|
|
32
|
-
"
|
|
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.
|
|
39
|
-
"@types/
|
|
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
|
-
"
|
|
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.
|
|
59
|
+
"zod": "^3.24.4"
|
|
56
60
|
},
|
|
57
61
|
"keywords": [
|
|
58
62
|
"typescript",
|