@cyanheads/git-mcp-server 2.1.0 → 2.1.2
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 +8 -11
- package/dist/config/index.js +7 -7
- package/dist/index.js +35 -21
- package/dist/mcp-server/server.js +72 -56
- package/dist/mcp-server/tools/gitAdd/index.js +1 -1
- package/dist/mcp-server/tools/gitAdd/logic.js +88 -39
- package/dist/mcp-server/tools/gitAdd/registration.js +17 -14
- package/dist/mcp-server/tools/gitBranch/index.js +1 -1
- package/dist/mcp-server/tools/gitBranch/logic.js +213 -85
- package/dist/mcp-server/tools/gitBranch/registration.js +16 -13
- package/dist/mcp-server/tools/gitCheckout/index.js +1 -1
- package/dist/mcp-server/tools/gitCheckout/logic.js +85 -145
- package/dist/mcp-server/tools/gitCheckout/registration.js +16 -14
- package/dist/mcp-server/tools/gitCherryPick/index.js +1 -1
- package/dist/mcp-server/tools/gitCherryPick/logic.js +100 -41
- package/dist/mcp-server/tools/gitCherryPick/registration.js +21 -14
- package/dist/mcp-server/tools/gitClean/index.js +1 -1
- package/dist/mcp-server/tools/gitClean/logic.js +93 -41
- package/dist/mcp-server/tools/gitClean/registration.js +19 -16
- package/dist/mcp-server/tools/gitClearWorkingDir/index.js +1 -1
- package/dist/mcp-server/tools/gitClearWorkingDir/logic.js +14 -11
- package/dist/mcp-server/tools/gitClearWorkingDir/registration.js +19 -13
- package/dist/mcp-server/tools/gitClone/index.js +1 -1
- package/dist/mcp-server/tools/gitClone/logic.js +89 -30
- package/dist/mcp-server/tools/gitClone/registration.js +15 -12
- package/dist/mcp-server/tools/gitCommit/index.js +1 -1
- package/dist/mcp-server/tools/gitCommit/logic.js +198 -76
- package/dist/mcp-server/tools/gitCommit/registration.js +23 -20
- package/dist/mcp-server/tools/gitDiff/index.js +1 -1
- package/dist/mcp-server/tools/gitDiff/logic.js +124 -44
- package/dist/mcp-server/tools/gitDiff/registration.js +16 -14
- package/dist/mcp-server/tools/gitFetch/index.js +1 -1
- package/dist/mcp-server/tools/gitFetch/logic.js +78 -49
- package/dist/mcp-server/tools/gitFetch/registration.js +16 -14
- package/dist/mcp-server/tools/gitInit/index.js +1 -1
- package/dist/mcp-server/tools/gitInit/logic.js +88 -34
- package/dist/mcp-server/tools/gitInit/registration.js +32 -18
- package/dist/mcp-server/tools/gitLog/index.js +1 -1
- package/dist/mcp-server/tools/gitLog/logic.js +133 -47
- package/dist/mcp-server/tools/gitLog/registration.js +16 -14
- package/dist/mcp-server/tools/gitMerge/index.js +1 -1
- package/dist/mcp-server/tools/gitMerge/logic.js +102 -61
- package/dist/mcp-server/tools/gitMerge/registration.js +17 -14
- package/dist/mcp-server/tools/gitPull/index.js +1 -1
- package/dist/mcp-server/tools/gitPull/logic.js +90 -69
- package/dist/mcp-server/tools/gitPull/registration.js +16 -14
- package/dist/mcp-server/tools/gitPush/index.js +1 -1
- package/dist/mcp-server/tools/gitPush/logic.js +116 -100
- package/dist/mcp-server/tools/gitPush/registration.js +16 -14
- package/dist/mcp-server/tools/gitRebase/index.js +1 -1
- package/dist/mcp-server/tools/gitRebase/logic.js +121 -82
- package/dist/mcp-server/tools/gitRebase/registration.js +21 -14
- package/dist/mcp-server/tools/gitRemote/index.js +1 -1
- package/dist/mcp-server/tools/gitRemote/logic.js +108 -52
- package/dist/mcp-server/tools/gitRemote/registration.js +14 -11
- package/dist/mcp-server/tools/gitReset/index.js +1 -1
- package/dist/mcp-server/tools/gitReset/logic.js +65 -37
- package/dist/mcp-server/tools/gitReset/registration.js +14 -12
- package/dist/mcp-server/tools/gitSetWorkingDir/index.js +1 -1
- package/dist/mcp-server/tools/gitSetWorkingDir/logic.js +74 -34
- package/dist/mcp-server/tools/gitSetWorkingDir/registration.js +18 -11
- package/dist/mcp-server/tools/gitShow/index.js +1 -1
- package/dist/mcp-server/tools/gitShow/logic.js +78 -35
- package/dist/mcp-server/tools/gitShow/registration.js +17 -12
- package/dist/mcp-server/tools/gitStash/index.js +1 -1
- package/dist/mcp-server/tools/gitStash/logic.js +143 -58
- package/dist/mcp-server/tools/gitStash/registration.js +19 -12
- package/dist/mcp-server/tools/gitStatus/index.js +1 -1
- package/dist/mcp-server/tools/gitStatus/logic.js +100 -58
- package/dist/mcp-server/tools/gitStatus/registration.js +15 -12
- package/dist/mcp-server/tools/gitTag/index.js +1 -1
- package/dist/mcp-server/tools/gitTag/logic.js +124 -51
- package/dist/mcp-server/tools/gitTag/registration.js +14 -11
- package/dist/mcp-server/tools/gitWorktree/index.js +1 -1
- package/dist/mcp-server/tools/gitWorktree/logic.js +204 -95
- package/dist/mcp-server/tools/gitWorktree/registration.js +14 -11
- package/dist/mcp-server/tools/gitWrapupInstructions/index.js +1 -1
- package/dist/mcp-server/tools/gitWrapupInstructions/logic.js +23 -11
- package/dist/mcp-server/tools/gitWrapupInstructions/registration.js +14 -12
- package/dist/mcp-server/transports/httpTransport.js +187 -79
- package/dist/mcp-server/transports/stdioTransport.js +14 -8
- package/dist/types-global/errors.js +9 -4
- package/dist/utils/index.js +4 -4
- package/dist/utils/internal/errorHandler.js +62 -40
- package/dist/utils/internal/index.js +3 -3
- package/dist/utils/internal/logger.js +97 -54
- package/dist/utils/internal/requestContext.js +7 -5
- package/dist/utils/metrics/index.js +1 -1
- package/dist/utils/metrics/tokenCounter.js +18 -14
- package/dist/utils/parsing/dateParser.js +5 -5
- package/dist/utils/parsing/index.js +2 -2
- package/dist/utils/parsing/jsonParser.js +20 -11
- package/dist/utils/security/idGenerator.js +8 -10
- package/dist/utils/security/index.js +3 -3
- package/dist/utils/security/rateLimiter.js +16 -14
- package/dist/utils/security/sanitization.js +139 -82
- package/package.json +45 -23
|
@@ -1,37 +1,61 @@
|
|
|
1
|
-
import { BaseErrorCode, McpError } from
|
|
2
|
-
import { logger } from
|
|
3
|
-
import { sanitizeInputForLogging } from
|
|
1
|
+
import { BaseErrorCode, McpError } from "../../types-global/errors.js"; // Corrected path
|
|
2
|
+
import { logger } from "./logger.js";
|
|
3
|
+
import { sanitizeInputForLogging } from "../index.js"; // Import from main barrel file
|
|
4
4
|
/**
|
|
5
5
|
* Simple mapper that maps error types to error codes
|
|
6
6
|
*/
|
|
7
7
|
const ERROR_TYPE_MAPPINGS = {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
SyntaxError: BaseErrorCode.VALIDATION_ERROR,
|
|
9
|
+
TypeError: BaseErrorCode.VALIDATION_ERROR,
|
|
10
|
+
ReferenceError: BaseErrorCode.INTERNAL_ERROR,
|
|
11
|
+
RangeError: BaseErrorCode.VALIDATION_ERROR,
|
|
12
|
+
URIError: BaseErrorCode.VALIDATION_ERROR,
|
|
13
|
+
EvalError: BaseErrorCode.INTERNAL_ERROR,
|
|
14
14
|
};
|
|
15
15
|
/**
|
|
16
16
|
* Common error patterns for automatic classification
|
|
17
17
|
*/
|
|
18
18
|
const COMMON_ERROR_PATTERNS = [
|
|
19
19
|
// Authentication related errors
|
|
20
|
-
{
|
|
20
|
+
{
|
|
21
|
+
pattern: /auth|unauthorized|unauthenticated|not.*logged.*in|invalid.*token|expired.*token/i,
|
|
22
|
+
errorCode: BaseErrorCode.UNAUTHORIZED,
|
|
23
|
+
},
|
|
21
24
|
// Permission related errors
|
|
22
|
-
{
|
|
25
|
+
{
|
|
26
|
+
pattern: /permission|forbidden|access.*denied|not.*allowed/i,
|
|
27
|
+
errorCode: BaseErrorCode.FORBIDDEN,
|
|
28
|
+
},
|
|
23
29
|
// Not found errors
|
|
24
|
-
{
|
|
30
|
+
{
|
|
31
|
+
pattern: /not.*found|missing|no.*such|doesn't.*exist|couldn't.*find/i,
|
|
32
|
+
errorCode: BaseErrorCode.NOT_FOUND,
|
|
33
|
+
},
|
|
25
34
|
// Validation errors
|
|
26
|
-
{
|
|
35
|
+
{
|
|
36
|
+
pattern: /invalid|validation|malformed|bad request|wrong format/i,
|
|
37
|
+
errorCode: BaseErrorCode.VALIDATION_ERROR,
|
|
38
|
+
},
|
|
27
39
|
// Conflict errors
|
|
28
|
-
{
|
|
40
|
+
{
|
|
41
|
+
pattern: /conflict|already.*exists|duplicate|unique.*constraint/i,
|
|
42
|
+
errorCode: BaseErrorCode.CONFLICT,
|
|
43
|
+
},
|
|
29
44
|
// Rate limiting
|
|
30
|
-
{
|
|
45
|
+
{
|
|
46
|
+
pattern: /rate.*limit|too.*many.*requests|throttled/i,
|
|
47
|
+
errorCode: BaseErrorCode.RATE_LIMITED,
|
|
48
|
+
},
|
|
31
49
|
// Timeout errors
|
|
32
|
-
{
|
|
50
|
+
{
|
|
51
|
+
pattern: /timeout|timed.*out|deadline.*exceeded/i,
|
|
52
|
+
errorCode: BaseErrorCode.TIMEOUT,
|
|
53
|
+
},
|
|
33
54
|
// External service errors
|
|
34
|
-
{
|
|
55
|
+
{
|
|
56
|
+
pattern: /service.*unavailable|bad.*gateway|gateway.*timeout/i,
|
|
57
|
+
errorCode: BaseErrorCode.SERVICE_UNAVAILABLE,
|
|
58
|
+
},
|
|
35
59
|
];
|
|
36
60
|
/**
|
|
37
61
|
* Get a readable name for an error
|
|
@@ -40,17 +64,15 @@ const COMMON_ERROR_PATTERNS = [
|
|
|
40
64
|
*/
|
|
41
65
|
function getErrorName(error) {
|
|
42
66
|
if (error instanceof Error) {
|
|
43
|
-
return error.name ||
|
|
67
|
+
return error.name || "Error";
|
|
44
68
|
}
|
|
45
69
|
if (error === null) {
|
|
46
|
-
return
|
|
70
|
+
return "NullError";
|
|
47
71
|
}
|
|
48
72
|
if (error === undefined) {
|
|
49
|
-
return
|
|
73
|
+
return "UndefinedError";
|
|
50
74
|
}
|
|
51
|
-
return typeof error ===
|
|
52
|
-
? 'ObjectError'
|
|
53
|
-
: 'UnknownError';
|
|
75
|
+
return typeof error === "object" ? "ObjectError" : "UnknownError";
|
|
54
76
|
}
|
|
55
77
|
/**
|
|
56
78
|
* Get a message from an error
|
|
@@ -62,14 +84,12 @@ function getErrorMessage(error) {
|
|
|
62
84
|
return error.message;
|
|
63
85
|
}
|
|
64
86
|
if (error === null) {
|
|
65
|
-
return
|
|
87
|
+
return "Null error occurred";
|
|
66
88
|
}
|
|
67
89
|
if (error === undefined) {
|
|
68
|
-
return
|
|
90
|
+
return "Undefined error occurred";
|
|
69
91
|
}
|
|
70
|
-
return typeof error ===
|
|
71
|
-
? error
|
|
72
|
-
: String(error);
|
|
92
|
+
return typeof error === "string" ? error : String(error);
|
|
73
93
|
}
|
|
74
94
|
/**
|
|
75
95
|
* Error handler utility class with various error handling methods
|
|
@@ -95,7 +115,7 @@ export class ErrorHandler {
|
|
|
95
115
|
for (const pattern of COMMON_ERROR_PATTERNS) {
|
|
96
116
|
const regex = pattern.pattern instanceof RegExp
|
|
97
117
|
? pattern.pattern
|
|
98
|
-
: new RegExp(pattern.pattern,
|
|
118
|
+
: new RegExp(pattern.pattern, "i");
|
|
99
119
|
if (regex.test(errorMessage) || regex.test(errorName)) {
|
|
100
120
|
return pattern.errorCode;
|
|
101
121
|
}
|
|
@@ -110,13 +130,15 @@ export class ErrorHandler {
|
|
|
110
130
|
* @returns The transformed error
|
|
111
131
|
*/
|
|
112
132
|
static handleError(error, options) {
|
|
113
|
-
const { context, operation, input, rethrow = false, errorCode: explicitErrorCode, includeStack = true, critical = false } = options;
|
|
133
|
+
const { context, operation, input, rethrow = false, errorCode: explicitErrorCode, includeStack = true, critical = false, } = options;
|
|
114
134
|
// If it's already an McpError, use it directly but apply additional context
|
|
115
135
|
if (error instanceof McpError) {
|
|
116
136
|
// Add any additional context
|
|
117
137
|
if (context && Object.keys(context).length > 0) {
|
|
118
138
|
// Ensure details is an object before spreading
|
|
119
|
-
const existingDetails = typeof error.details ===
|
|
139
|
+
const existingDetails = typeof error.details === "object" && error.details !== null
|
|
140
|
+
? error.details
|
|
141
|
+
: {};
|
|
120
142
|
error.details = { ...existingDetails, ...context };
|
|
121
143
|
}
|
|
122
144
|
// Log the error with sanitized input
|
|
@@ -126,7 +148,7 @@ export class ErrorHandler {
|
|
|
126
148
|
input: input ? sanitizeInputForLogging(input) : undefined,
|
|
127
149
|
stack: includeStack ? error.stack : undefined,
|
|
128
150
|
critical,
|
|
129
|
-
...context
|
|
151
|
+
...context,
|
|
130
152
|
});
|
|
131
153
|
if (rethrow) {
|
|
132
154
|
throw error;
|
|
@@ -144,7 +166,7 @@ export class ErrorHandler {
|
|
|
144
166
|
requestId: context?.requestId,
|
|
145
167
|
stack: includeStack && error instanceof Error ? error.stack : undefined,
|
|
146
168
|
critical,
|
|
147
|
-
...context
|
|
169
|
+
...context,
|
|
148
170
|
});
|
|
149
171
|
// Choose the error code (explicit > determined > default)
|
|
150
172
|
const errorCode = explicitErrorCode ||
|
|
@@ -159,7 +181,7 @@ export class ErrorHandler {
|
|
|
159
181
|
transformedError = new McpError(errorCode, `Error ${operation}: ${getErrorMessage(error)}`, // Use helper function
|
|
160
182
|
{
|
|
161
183
|
originalError: getErrorName(error),
|
|
162
|
-
...context
|
|
184
|
+
...context,
|
|
163
185
|
});
|
|
164
186
|
}
|
|
165
187
|
// Rethrow if requested
|
|
@@ -198,9 +220,7 @@ export class ErrorHandler {
|
|
|
198
220
|
if (defaultFactory) {
|
|
199
221
|
return defaultFactory(error);
|
|
200
222
|
}
|
|
201
|
-
return error instanceof Error
|
|
202
|
-
? error
|
|
203
|
-
: new Error(String(error));
|
|
223
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
204
224
|
}
|
|
205
225
|
// Removed createErrorMapper method for simplification
|
|
206
226
|
/**
|
|
@@ -214,20 +234,22 @@ export class ErrorHandler {
|
|
|
214
234
|
code: error.code,
|
|
215
235
|
message: error.message,
|
|
216
236
|
// Ensure details is an object
|
|
217
|
-
details: typeof error.details ===
|
|
237
|
+
details: typeof error.details === "object" && error.details !== null
|
|
238
|
+
? error.details
|
|
239
|
+
: {},
|
|
218
240
|
};
|
|
219
241
|
}
|
|
220
242
|
if (error instanceof Error) {
|
|
221
243
|
return {
|
|
222
244
|
code: ErrorHandler.determineErrorCode(error),
|
|
223
245
|
message: error.message,
|
|
224
|
-
details: { errorType: error.name }
|
|
246
|
+
details: { errorType: error.name },
|
|
225
247
|
};
|
|
226
248
|
}
|
|
227
249
|
return {
|
|
228
250
|
code: BaseErrorCode.UNKNOWN_ERROR,
|
|
229
251
|
message: String(error),
|
|
230
|
-
details: { errorType: typeof error }
|
|
252
|
+
details: { errorType: typeof error },
|
|
231
253
|
};
|
|
232
254
|
}
|
|
233
255
|
/**
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export * from
|
|
2
|
-
export * from
|
|
3
|
-
export * from
|
|
1
|
+
export * from "./errorHandler.js";
|
|
2
|
+
export * from "./logger.js";
|
|
3
|
+
export * from "./requestContext.js";
|
|
@@ -1,35 +1,43 @@
|
|
|
1
|
-
import fs from
|
|
2
|
-
import path from
|
|
3
|
-
import { fileURLToPath } from
|
|
4
|
-
import winston from
|
|
5
|
-
import { config } from
|
|
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
6
|
// Define the numeric severity for comparison (lower is more severe)
|
|
7
7
|
const mcpLevelSeverity = {
|
|
8
|
-
emerg: 0,
|
|
8
|
+
emerg: 0,
|
|
9
|
+
alert: 1,
|
|
10
|
+
crit: 2,
|
|
11
|
+
error: 3,
|
|
12
|
+
warning: 4,
|
|
13
|
+
notice: 5,
|
|
14
|
+
info: 6,
|
|
15
|
+
debug: 7,
|
|
9
16
|
};
|
|
10
17
|
// Map MCP levels to Winston's core levels for file logging
|
|
11
18
|
const mcpToWinstonLevel = {
|
|
12
|
-
debug:
|
|
13
|
-
info:
|
|
14
|
-
notice:
|
|
15
|
-
warning:
|
|
16
|
-
error:
|
|
17
|
-
crit:
|
|
18
|
-
alert:
|
|
19
|
-
emerg:
|
|
19
|
+
debug: "debug",
|
|
20
|
+
info: "info",
|
|
21
|
+
notice: "info", // Map notice to info for file logging
|
|
22
|
+
warning: "warn",
|
|
23
|
+
error: "error",
|
|
24
|
+
crit: "error", // Map critical levels to error for file logging
|
|
25
|
+
alert: "error",
|
|
26
|
+
emerg: "error",
|
|
20
27
|
};
|
|
21
28
|
// Resolve __dirname for ESM
|
|
22
29
|
const __filename = fileURLToPath(import.meta.url);
|
|
23
30
|
const __dirname = path.dirname(__filename);
|
|
24
31
|
// Calculate project root robustly (works from src/ or dist/)
|
|
25
|
-
const isRunningFromDist = __dirname.includes(path.sep +
|
|
32
|
+
const isRunningFromDist = __dirname.includes(path.sep + "dist" + path.sep);
|
|
26
33
|
const levelsToGoUp = isRunningFromDist ? 3 : 2;
|
|
27
|
-
const pathSegments = Array(levelsToGoUp).fill(
|
|
34
|
+
const pathSegments = Array(levelsToGoUp).fill("..");
|
|
28
35
|
const projectRoot = path.resolve(__dirname, ...pathSegments);
|
|
29
|
-
const logsDir = path.join(projectRoot,
|
|
36
|
+
const logsDir = path.join(projectRoot, "logs");
|
|
30
37
|
// Security: ensure logsDir is within projectRoot
|
|
31
38
|
const resolvedLogsDir = path.resolve(logsDir);
|
|
32
|
-
const isLogsDirSafe = resolvedLogsDir === projectRoot ||
|
|
39
|
+
const isLogsDirSafe = resolvedLogsDir === projectRoot ||
|
|
40
|
+
resolvedLogsDir.startsWith(projectRoot + path.sep);
|
|
33
41
|
if (!isLogsDirSafe) {
|
|
34
42
|
// Use console.error for critical pre-init errors.
|
|
35
43
|
// Only log to console if TTY to avoid polluting stdout for stdio MCP clients.
|
|
@@ -42,21 +50,24 @@ if (!isLogsDirSafe) {
|
|
|
42
50
|
* This is extracted to avoid duplication between initialize and setLevel.
|
|
43
51
|
*/
|
|
44
52
|
function createWinstonConsoleFormat() {
|
|
45
|
-
return winston.format.combine(winston.format.colorize(), winston.format.timestamp({ format:
|
|
46
|
-
let metaString =
|
|
53
|
+
return winston.format.combine(winston.format.colorize(), winston.format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }), winston.format.printf(({ timestamp, level, message, ...meta }) => {
|
|
54
|
+
let metaString = "";
|
|
47
55
|
const metaCopy = { ...meta };
|
|
48
|
-
if (metaCopy.error && typeof metaCopy.error ===
|
|
56
|
+
if (metaCopy.error && typeof metaCopy.error === "object") {
|
|
49
57
|
const errorObj = metaCopy.error;
|
|
50
58
|
if (errorObj.message)
|
|
51
59
|
metaString += `\n Error: ${errorObj.message}`;
|
|
52
60
|
if (errorObj.stack)
|
|
53
|
-
metaString += `\n Stack: ${String(errorObj.stack)
|
|
61
|
+
metaString += `\n Stack: ${String(errorObj.stack)
|
|
62
|
+
.split("\n")
|
|
63
|
+
.map((l) => ` ${l}`)
|
|
64
|
+
.join("\n")}`;
|
|
54
65
|
delete metaCopy.error;
|
|
55
66
|
}
|
|
56
67
|
if (Object.keys(metaCopy).length > 0) {
|
|
57
68
|
try {
|
|
58
69
|
const remainingMetaJson = JSON.stringify(metaCopy, null, 2);
|
|
59
|
-
if (remainingMetaJson !==
|
|
70
|
+
if (remainingMetaJson !== "{}")
|
|
60
71
|
metaString += `\n Meta: ${remainingMetaJson}`;
|
|
61
72
|
}
|
|
62
73
|
catch (stringifyError) {
|
|
@@ -75,17 +86,17 @@ class Logger {
|
|
|
75
86
|
winstonLogger;
|
|
76
87
|
initialized = false;
|
|
77
88
|
mcpNotificationSender;
|
|
78
|
-
currentMcpLevel =
|
|
79
|
-
currentWinstonLevel =
|
|
89
|
+
currentMcpLevel = "info"; // Default MCP level
|
|
90
|
+
currentWinstonLevel = "info"; // Default Winston level
|
|
80
91
|
constructor() { }
|
|
81
92
|
/**
|
|
82
93
|
* Initialize Winston logger for file transport. Must be called once at app start.
|
|
83
94
|
* Console transport is added conditionally.
|
|
84
95
|
* @param level Initial minimum level to log ('info' default).
|
|
85
96
|
*/
|
|
86
|
-
async initialize(level =
|
|
97
|
+
async initialize(level = "info") {
|
|
87
98
|
if (this.initialized) {
|
|
88
|
-
this.warning(
|
|
99
|
+
this.warning("Logger already initialized.", { loggerSetup: true });
|
|
89
100
|
return;
|
|
90
101
|
}
|
|
91
102
|
this.currentMcpLevel = level;
|
|
@@ -111,7 +122,26 @@ class Logger {
|
|
|
111
122
|
const transports = [];
|
|
112
123
|
// Add file transports only if the directory is safe
|
|
113
124
|
if (isLogsDirSafe) {
|
|
114
|
-
transports.push(new winston.transports.File({
|
|
125
|
+
transports.push(new winston.transports.File({
|
|
126
|
+
filename: path.join(resolvedLogsDir, "error.log"),
|
|
127
|
+
level: "error",
|
|
128
|
+
format: fileFormat,
|
|
129
|
+
}), new winston.transports.File({
|
|
130
|
+
filename: path.join(resolvedLogsDir, "warn.log"),
|
|
131
|
+
level: "warn",
|
|
132
|
+
format: fileFormat,
|
|
133
|
+
}), new winston.transports.File({
|
|
134
|
+
filename: path.join(resolvedLogsDir, "info.log"),
|
|
135
|
+
level: "info",
|
|
136
|
+
format: fileFormat,
|
|
137
|
+
}), new winston.transports.File({
|
|
138
|
+
filename: path.join(resolvedLogsDir, "debug.log"),
|
|
139
|
+
level: "debug",
|
|
140
|
+
format: fileFormat,
|
|
141
|
+
}), new winston.transports.File({
|
|
142
|
+
filename: path.join(resolvedLogsDir, "combined.log"),
|
|
143
|
+
format: fileFormat,
|
|
144
|
+
}));
|
|
115
145
|
}
|
|
116
146
|
else {
|
|
117
147
|
// Conditional console output for pre-init warnings.
|
|
@@ -124,22 +154,24 @@ class Logger {
|
|
|
124
154
|
// Conditionally add Console transport only if:
|
|
125
155
|
// 1. MCP level is 'debug'
|
|
126
156
|
// 2. stdout is a TTY (interactive terminal, not piped)
|
|
127
|
-
if (this.currentMcpLevel ===
|
|
157
|
+
if (this.currentMcpLevel === "debug" && process.stdout.isTTY) {
|
|
128
158
|
const consoleFormat = createWinstonConsoleFormat();
|
|
129
159
|
transports.push(new winston.transports.Console({
|
|
130
|
-
level:
|
|
160
|
+
level: "debug",
|
|
131
161
|
format: consoleFormat,
|
|
132
162
|
}));
|
|
133
|
-
consoleLoggingEnabledMessage =
|
|
163
|
+
consoleLoggingEnabledMessage =
|
|
164
|
+
"Console logging enabled at level: debug (stdout is TTY)";
|
|
134
165
|
}
|
|
135
|
-
else if (this.currentMcpLevel ===
|
|
136
|
-
consoleLoggingSkippedMessage =
|
|
166
|
+
else if (this.currentMcpLevel === "debug" && !process.stdout.isTTY) {
|
|
167
|
+
consoleLoggingSkippedMessage =
|
|
168
|
+
"Console logging skipped: Level is debug, but stdout is not a TTY (likely stdio transport).";
|
|
137
169
|
}
|
|
138
170
|
// Create logger with the initial Winston level and configured transports
|
|
139
171
|
this.winstonLogger = winston.createLogger({
|
|
140
172
|
level: this.currentWinstonLevel,
|
|
141
173
|
transports,
|
|
142
|
-
exitOnError: false
|
|
174
|
+
exitOnError: false,
|
|
143
175
|
});
|
|
144
176
|
// Log deferred messages now that winstonLogger is initialized
|
|
145
177
|
if (logsDirCreatedMessage) {
|
|
@@ -153,14 +185,14 @@ class Logger {
|
|
|
153
185
|
}
|
|
154
186
|
this.initialized = true;
|
|
155
187
|
await Promise.resolve(); // Yield to event loop
|
|
156
|
-
this.info(`Logger initialized. File logging level: ${this.currentWinstonLevel}. MCP logging level: ${this.currentMcpLevel}. Console logging: ${process.stdout.isTTY && this.currentMcpLevel ===
|
|
188
|
+
this.info(`Logger initialized. File logging level: ${this.currentWinstonLevel}. MCP logging level: ${this.currentMcpLevel}. Console logging: ${process.stdout.isTTY && this.currentMcpLevel === "debug" ? "enabled" : "disabled"}`, { loggerSetup: true });
|
|
157
189
|
}
|
|
158
190
|
/**
|
|
159
191
|
* Sets the function used to send MCP 'notifications/message'.
|
|
160
192
|
*/
|
|
161
193
|
setMcpNotificationSender(sender) {
|
|
162
194
|
this.mcpNotificationSender = sender;
|
|
163
|
-
const status = sender ?
|
|
195
|
+
const status = sender ? "enabled" : "disabled";
|
|
164
196
|
this.info(`MCP notification sending ${status}.`, { loggerSetup: true });
|
|
165
197
|
}
|
|
166
198
|
/**
|
|
@@ -183,21 +215,24 @@ class Logger {
|
|
|
183
215
|
this.currentWinstonLevel = mcpToWinstonLevel[newLevel];
|
|
184
216
|
this.winstonLogger.level = this.currentWinstonLevel;
|
|
185
217
|
// Add or remove console transport based on the new level and TTY status
|
|
186
|
-
const consoleTransport = this.winstonLogger.transports.find(t => t instanceof winston.transports.Console);
|
|
187
|
-
const shouldHaveConsole = newLevel ===
|
|
218
|
+
const consoleTransport = this.winstonLogger.transports.find((t) => t instanceof winston.transports.Console);
|
|
219
|
+
const shouldHaveConsole = newLevel === "debug" && process.stdout.isTTY;
|
|
188
220
|
if (shouldHaveConsole && !consoleTransport) {
|
|
189
221
|
// Add console transport
|
|
190
222
|
const consoleFormat = createWinstonConsoleFormat();
|
|
191
|
-
this.winstonLogger.add(new winston.transports.Console({
|
|
192
|
-
|
|
223
|
+
this.winstonLogger.add(new winston.transports.Console({
|
|
224
|
+
level: "debug",
|
|
225
|
+
format: consoleFormat,
|
|
226
|
+
}));
|
|
227
|
+
this.info("Console logging dynamically enabled.", { loggerSetup: true });
|
|
193
228
|
}
|
|
194
229
|
else if (!shouldHaveConsole && consoleTransport) {
|
|
195
230
|
// Remove console transport
|
|
196
231
|
this.winstonLogger.remove(consoleTransport);
|
|
197
|
-
this.info(
|
|
232
|
+
this.info("Console logging dynamically disabled.", { loggerSetup: true });
|
|
198
233
|
}
|
|
199
234
|
if (oldLevel !== newLevel) {
|
|
200
|
-
this.info(`Log level changed. File logging level: ${this.currentWinstonLevel}. MCP logging level: ${this.currentMcpLevel}. Console logging: ${shouldHaveConsole ?
|
|
235
|
+
this.info(`Log level changed. File logging level: ${this.currentWinstonLevel}. MCP logging level: ${this.currentMcpLevel}. Console logging: ${shouldHaveConsole ? "enabled" : "disabled"}`, { loggerSetup: true });
|
|
201
236
|
}
|
|
202
237
|
}
|
|
203
238
|
/** Get singleton instance. */
|
|
@@ -212,7 +247,7 @@ class Logger {
|
|
|
212
247
|
if (!this.initialized || !this.winstonLogger) {
|
|
213
248
|
// Conditional console output if logger not usable.
|
|
214
249
|
if (process.stdout.isTTY) {
|
|
215
|
-
console.warn(
|
|
250
|
+
console.warn("Logger not initialized; message dropped.");
|
|
216
251
|
}
|
|
217
252
|
return false;
|
|
218
253
|
}
|
|
@@ -239,7 +274,7 @@ class Logger {
|
|
|
239
274
|
mcpDataPayload.context = context;
|
|
240
275
|
if (error) {
|
|
241
276
|
mcpDataPayload.error = { message: error.message };
|
|
242
|
-
if (this.currentMcpLevel ===
|
|
277
|
+
if (this.currentMcpLevel === "debug" && error.stack) {
|
|
243
278
|
mcpDataPayload.error.stack = error.stack.substring(0, 500);
|
|
244
279
|
}
|
|
245
280
|
}
|
|
@@ -251,38 +286,46 @@ class Logger {
|
|
|
251
286
|
originalLevel: level,
|
|
252
287
|
originalMessage: msg,
|
|
253
288
|
sendError: sendError instanceof Error ? sendError.message : String(sendError),
|
|
254
|
-
mcpPayload: mcpDataPayload
|
|
289
|
+
mcpPayload: mcpDataPayload,
|
|
255
290
|
});
|
|
256
291
|
}
|
|
257
292
|
}
|
|
258
293
|
}
|
|
259
294
|
// --- Public Logging Methods ---
|
|
260
|
-
debug(msg, context) {
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
295
|
+
debug(msg, context) {
|
|
296
|
+
this.log("debug", msg, context);
|
|
297
|
+
}
|
|
298
|
+
info(msg, context) {
|
|
299
|
+
this.log("info", msg, context);
|
|
300
|
+
}
|
|
301
|
+
notice(msg, context) {
|
|
302
|
+
this.log("notice", msg, context);
|
|
303
|
+
}
|
|
304
|
+
warning(msg, context) {
|
|
305
|
+
this.log("warning", msg, context);
|
|
306
|
+
}
|
|
264
307
|
error(msg, err, context) {
|
|
265
308
|
const errorObj = err instanceof Error ? err : undefined;
|
|
266
309
|
const combinedContext = err instanceof Error ? context : { ...(err || {}), ...(context || {}) };
|
|
267
|
-
this.log(
|
|
310
|
+
this.log("error", msg, combinedContext, errorObj);
|
|
268
311
|
}
|
|
269
312
|
crit(msg, err, context) {
|
|
270
313
|
const errorObj = err instanceof Error ? err : undefined;
|
|
271
314
|
const combinedContext = err instanceof Error ? context : { ...(err || {}), ...(context || {}) };
|
|
272
|
-
this.log(
|
|
315
|
+
this.log("crit", msg, combinedContext, errorObj);
|
|
273
316
|
}
|
|
274
317
|
alert(msg, err, context) {
|
|
275
318
|
const errorObj = err instanceof Error ? err : undefined;
|
|
276
319
|
const combinedContext = err instanceof Error ? context : { ...(err || {}), ...(context || {}) };
|
|
277
|
-
this.log(
|
|
320
|
+
this.log("alert", msg, combinedContext, errorObj);
|
|
278
321
|
}
|
|
279
322
|
emerg(msg, err, context) {
|
|
280
323
|
const errorObj = err instanceof Error ? err : undefined;
|
|
281
324
|
const combinedContext = err instanceof Error ? context : { ...(err || {}), ...(context || {}) };
|
|
282
|
-
this.log(
|
|
325
|
+
this.log("emerg", msg, combinedContext, errorObj);
|
|
283
326
|
}
|
|
284
327
|
fatal(msg, context, error) {
|
|
285
|
-
this.log(
|
|
328
|
+
this.log("emerg", msg, context, error);
|
|
286
329
|
}
|
|
287
330
|
}
|
|
288
331
|
// Export singleton instance
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { logger } from
|
|
1
|
+
import { logger } from "./logger.js";
|
|
2
2
|
// Import utils from the main barrel file (generateUUID from ../security/idGenerator.js)
|
|
3
|
-
import { generateUUID } from
|
|
3
|
+
import { generateUUID } from "../index.js";
|
|
4
4
|
// Direct instance for request context utilities
|
|
5
5
|
const requestContextServiceInstance = {
|
|
6
6
|
config: {},
|
|
@@ -12,9 +12,11 @@ const requestContextServiceInstance = {
|
|
|
12
12
|
configure(config) {
|
|
13
13
|
this.config = {
|
|
14
14
|
...this.config,
|
|
15
|
-
...config
|
|
15
|
+
...config,
|
|
16
16
|
};
|
|
17
|
-
logger.debug(
|
|
17
|
+
logger.debug("RequestContext configuration updated", {
|
|
18
|
+
config: this.config,
|
|
19
|
+
});
|
|
18
20
|
return { ...this.config };
|
|
19
21
|
},
|
|
20
22
|
/**
|
|
@@ -35,7 +37,7 @@ const requestContextServiceInstance = {
|
|
|
35
37
|
return {
|
|
36
38
|
requestId,
|
|
37
39
|
timestamp,
|
|
38
|
-
...additionalContext
|
|
40
|
+
...additionalContext,
|
|
39
41
|
};
|
|
40
42
|
},
|
|
41
43
|
// generateSecureRandomString function removed as it was unused and redundant
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from
|
|
1
|
+
export * from "./tokenCounter.js";
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { encoding_for_model } from
|
|
2
|
-
import { BaseErrorCode } from
|
|
1
|
+
import { encoding_for_model } from "tiktoken";
|
|
2
|
+
import { BaseErrorCode } from "../../types-global/errors.js";
|
|
3
3
|
// Import utils from the main barrel file (ErrorHandler, logger, RequestContext from ../internal/*)
|
|
4
|
-
import { ErrorHandler, logger } from
|
|
4
|
+
import { ErrorHandler, logger } from "../index.js";
|
|
5
5
|
// Define the model used specifically for token counting
|
|
6
|
-
const TOKENIZATION_MODEL =
|
|
6
|
+
const TOKENIZATION_MODEL = "gpt-4o"; // Note this is strictly for token counting, not the model used for inference
|
|
7
7
|
/**
|
|
8
8
|
* Calculates the number of tokens for a given text using the 'gpt-4o' tokenizer.
|
|
9
9
|
* Uses ErrorHandler for consistent error management.
|
|
@@ -27,11 +27,11 @@ export async function countTokens(text, context) {
|
|
|
27
27
|
encoding?.free(); // Ensure the encoder is freed if it was successfully created
|
|
28
28
|
}
|
|
29
29
|
}, {
|
|
30
|
-
operation:
|
|
30
|
+
operation: "countTokens",
|
|
31
31
|
context: context,
|
|
32
|
-
input: { textSample: text.substring(0, 50) +
|
|
32
|
+
input: { textSample: text.substring(0, 50) + "..." }, // Log sanitized input
|
|
33
33
|
errorCode: BaseErrorCode.INTERNAL_ERROR, // Use INTERNAL_ERROR for external lib issues
|
|
34
|
-
rethrow: true // Rethrow as McpError
|
|
34
|
+
rethrow: true, // Rethrow as McpError
|
|
35
35
|
// Removed onErrorReturn as we now rethrow
|
|
36
36
|
});
|
|
37
37
|
}
|
|
@@ -65,13 +65,13 @@ context) {
|
|
|
65
65
|
// Encode role
|
|
66
66
|
num_tokens += encoding.encode(message.role).length;
|
|
67
67
|
// Encode content - handle potential null or array content (vision)
|
|
68
|
-
if (typeof message.content ===
|
|
68
|
+
if (typeof message.content === "string") {
|
|
69
69
|
num_tokens += encoding.encode(message.content).length;
|
|
70
70
|
}
|
|
71
71
|
else if (Array.isArray(message.content)) {
|
|
72
72
|
// Handle multi-part content (e.g., text + image) - simplified: encode text parts only
|
|
73
73
|
for (const part of message.content) {
|
|
74
|
-
if (part.type ===
|
|
74
|
+
if (part.type === "text") {
|
|
75
75
|
num_tokens += encoding.encode(part.text).length;
|
|
76
76
|
}
|
|
77
77
|
else {
|
|
@@ -83,13 +83,15 @@ context) {
|
|
|
83
83
|
}
|
|
84
84
|
} // else: content is null, add 0 tokens
|
|
85
85
|
// Encode name if present (often associated with 'tool' or 'function' roles in newer models)
|
|
86
|
-
if (
|
|
86
|
+
if ("name" in message && message.name) {
|
|
87
87
|
num_tokens += tokens_per_name;
|
|
88
88
|
num_tokens += encoding.encode(message.name).length;
|
|
89
89
|
}
|
|
90
90
|
// --- Handle tool calls (specific to newer models) ---
|
|
91
91
|
// Assistant message requesting tool calls
|
|
92
|
-
if (message.role ===
|
|
92
|
+
if (message.role === "assistant" &&
|
|
93
|
+
"tool_calls" in message &&
|
|
94
|
+
message.tool_calls) {
|
|
93
95
|
for (const tool_call of message.tool_calls) {
|
|
94
96
|
// Add tokens for the function name and arguments
|
|
95
97
|
if (tool_call.function.name) {
|
|
@@ -102,7 +104,9 @@ context) {
|
|
|
102
104
|
}
|
|
103
105
|
}
|
|
104
106
|
// Tool message providing results
|
|
105
|
-
if (message.role ===
|
|
107
|
+
if (message.role === "tool" &&
|
|
108
|
+
"tool_call_id" in message &&
|
|
109
|
+
message.tool_call_id) {
|
|
106
110
|
num_tokens += encoding.encode(message.tool_call_id).length;
|
|
107
111
|
// Content of the tool message (the result) is already handled by the string content check above
|
|
108
112
|
}
|
|
@@ -114,11 +118,11 @@ context) {
|
|
|
114
118
|
encoding?.free();
|
|
115
119
|
}
|
|
116
120
|
}, {
|
|
117
|
-
operation:
|
|
121
|
+
operation: "countChatTokens",
|
|
118
122
|
context: context,
|
|
119
123
|
input: { messageCount: messages.length }, // Log sanitized input
|
|
120
124
|
errorCode: BaseErrorCode.INTERNAL_ERROR, // Use INTERNAL_ERROR
|
|
121
|
-
rethrow: true // Rethrow as McpError
|
|
125
|
+
rethrow: true, // Rethrow as McpError
|
|
122
126
|
// Removed onErrorReturn
|
|
123
127
|
});
|
|
124
128
|
}
|