@cyanheads/git-mcp-server 2.1.1 → 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 +21 -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 +42 -20
|
@@ -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
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import * as chrono from
|
|
1
|
+
import * as chrono from "chrono-node";
|
|
2
2
|
// Import utils from the main barrel file (logger, ErrorHandler, RequestContext from ../internal/*)
|
|
3
|
-
import { logger, ErrorHandler } from
|
|
4
|
-
import { BaseErrorCode } from
|
|
3
|
+
import { logger, ErrorHandler } from "../index.js";
|
|
4
|
+
import { BaseErrorCode } from "../../types-global/errors.js"; // Corrected path
|
|
5
5
|
/**
|
|
6
6
|
* Parses a natural language date string into a Date object.
|
|
7
7
|
*
|
|
@@ -12,7 +12,7 @@ import { BaseErrorCode } from '../../types-global/errors.js'; // Corrected path
|
|
|
12
12
|
* @throws McpError if parsing fails unexpectedly.
|
|
13
13
|
*/
|
|
14
14
|
async function parseDateString(text, context, refDate) {
|
|
15
|
-
const operation =
|
|
15
|
+
const operation = "parseDateString";
|
|
16
16
|
const logContext = { ...context, operation, inputText: text, refDate };
|
|
17
17
|
logger.debug(`Attempting to parse date string: "${text}"`, logContext);
|
|
18
18
|
return await ErrorHandler.tryCatch(async () => {
|
|
@@ -42,7 +42,7 @@ async function parseDateString(text, context, refDate) {
|
|
|
42
42
|
* @throws McpError if parsing fails unexpectedly.
|
|
43
43
|
*/
|
|
44
44
|
async function parseDateStringDetailed(text, context, refDate) {
|
|
45
|
-
const operation =
|
|
45
|
+
const operation = "parseDateStringDetailed";
|
|
46
46
|
const logContext = { ...context, operation, inputText: text, refDate };
|
|
47
47
|
logger.debug(`Attempting detailed parse of date string: "${text}"`, logContext);
|
|
48
48
|
return await ErrorHandler.tryCatch(async () => {
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export * from
|
|
2
|
-
export * from
|
|
1
|
+
export * from "./jsonParser.js";
|
|
2
|
+
export * from "./dateParser.js";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { parse as parsePartialJson, Allow as PartialJsonAllow } from
|
|
2
|
-
import { BaseErrorCode, McpError } from
|
|
1
|
+
import { parse as parsePartialJson, Allow as PartialJsonAllow, } from "partial-json";
|
|
2
|
+
import { BaseErrorCode, McpError } from "../../types-global/errors.js";
|
|
3
3
|
// Import utils from the main barrel file (logger, RequestContext from ../internal/*)
|
|
4
|
-
import { logger } from
|
|
4
|
+
import { logger } from "../index.js";
|
|
5
5
|
/**
|
|
6
6
|
* Enum mirroring partial-json's Allow constants for specifying
|
|
7
7
|
* what types of partial JSON structures are permissible during parsing.
|
|
@@ -34,10 +34,13 @@ class JsonParser {
|
|
|
34
34
|
const thinkContent = match[1].trim();
|
|
35
35
|
const restOfString = match[2];
|
|
36
36
|
if (thinkContent) {
|
|
37
|
-
logger.debug(
|
|
37
|
+
logger.debug("LLM <think> block detected and logged.", {
|
|
38
|
+
...context,
|
|
39
|
+
thinkContent,
|
|
40
|
+
});
|
|
38
41
|
}
|
|
39
42
|
else {
|
|
40
|
-
logger.debug(
|
|
43
|
+
logger.debug("Empty LLM <think> block detected.", context);
|
|
41
44
|
}
|
|
42
45
|
stringToParse = restOfString; // Parse only the part after </think>
|
|
43
46
|
}
|
|
@@ -45,17 +48,18 @@ class JsonParser {
|
|
|
45
48
|
stringToParse = stringToParse.trim();
|
|
46
49
|
if (!stringToParse) {
|
|
47
50
|
// If after removing think block and trimming, the string is empty, it's an error
|
|
48
|
-
throw new McpError(BaseErrorCode.VALIDATION_ERROR,
|
|
51
|
+
throw new McpError(BaseErrorCode.VALIDATION_ERROR, "JSON string is empty after removing <think> block.", context);
|
|
49
52
|
}
|
|
50
53
|
try {
|
|
51
54
|
// Ensure the string starts with '{' or '[' if we expect an object or array after stripping <think>
|
|
52
55
|
// This helps catch cases where only non-JSON text remains.
|
|
53
|
-
if (!stringToParse.startsWith(
|
|
56
|
+
if (!stringToParse.startsWith("{") && !stringToParse.startsWith("[")) {
|
|
54
57
|
// Check if it might be a simple string value that partial-json could parse
|
|
55
58
|
// Allow simple strings only if specifically permitted or Allow.ALL is used
|
|
56
59
|
const allowsString = (allowPartial & Allow.STR) === Allow.STR;
|
|
57
|
-
if (!allowsString && !stringToParse.startsWith('"')) {
|
|
58
|
-
|
|
60
|
+
if (!allowsString && !stringToParse.startsWith('"')) {
|
|
61
|
+
// Allow quoted strings if Allow.STR is set
|
|
62
|
+
throw new Error("Remaining content does not appear to be valid JSON object or array.");
|
|
59
63
|
}
|
|
60
64
|
// If it starts with a quote and strings are allowed, let parsePartialJson handle it
|
|
61
65
|
}
|
|
@@ -64,11 +68,16 @@ class JsonParser {
|
|
|
64
68
|
catch (error) {
|
|
65
69
|
// Wrap the original error in an McpError for consistent error handling
|
|
66
70
|
// Include the original error message for better debugging context.
|
|
67
|
-
logger.error(
|
|
71
|
+
logger.error("Failed to parse JSON content.", {
|
|
72
|
+
...context,
|
|
73
|
+
error: error.message,
|
|
74
|
+
contentAttempted: stringToParse,
|
|
75
|
+
});
|
|
68
76
|
throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Failed to parse JSON: ${error.message}`, {
|
|
77
|
+
// Combine context and details into the third argument
|
|
69
78
|
...context,
|
|
70
79
|
originalContent: stringToParse,
|
|
71
|
-
rawError: error instanceof Error ? error.stack : String(error) // Include raw error info
|
|
80
|
+
rawError: error instanceof Error ? error.stack : String(error), // Include raw error info
|
|
72
81
|
});
|
|
73
82
|
}
|
|
74
83
|
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { randomBytes, randomUUID as cryptoRandomUUID } from
|
|
2
|
-
import { BaseErrorCode, McpError } from
|
|
1
|
+
import { randomBytes, randomUUID as cryptoRandomUUID } from "crypto"; // Import cryptoRandomUUID
|
|
2
|
+
import { BaseErrorCode, McpError } from "../../types-global/errors.js"; // Corrected path
|
|
3
3
|
/**
|
|
4
4
|
* Generic ID Generator class for creating and managing unique identifiers
|
|
5
5
|
*/
|
|
6
6
|
export class IdGenerator {
|
|
7
7
|
// Default charset
|
|
8
|
-
static DEFAULT_CHARSET =
|
|
8
|
+
static DEFAULT_CHARSET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
|
9
9
|
// Default separator
|
|
10
|
-
static DEFAULT_SEPARATOR =
|
|
10
|
+
static DEFAULT_SEPARATOR = "_";
|
|
11
11
|
// Default random part length
|
|
12
12
|
static DEFAULT_LENGTH = 6;
|
|
13
13
|
// Entity prefixes
|
|
@@ -50,7 +50,7 @@ export class IdGenerator {
|
|
|
50
50
|
*/
|
|
51
51
|
generateRandomString(length = IdGenerator.DEFAULT_LENGTH, charset = IdGenerator.DEFAULT_CHARSET) {
|
|
52
52
|
const bytes = randomBytes(length);
|
|
53
|
-
let result =
|
|
53
|
+
let result = "";
|
|
54
54
|
for (let i = 0; i < length; i++) {
|
|
55
55
|
result += charset[bytes[i] % charset.length];
|
|
56
56
|
}
|
|
@@ -63,11 +63,9 @@ export class IdGenerator {
|
|
|
63
63
|
* @returns A unique identifier string
|
|
64
64
|
*/
|
|
65
65
|
generate(prefix, options = {}) {
|
|
66
|
-
const { length = IdGenerator.DEFAULT_LENGTH, separator = IdGenerator.DEFAULT_SEPARATOR, charset = IdGenerator.DEFAULT_CHARSET } = options;
|
|
66
|
+
const { length = IdGenerator.DEFAULT_LENGTH, separator = IdGenerator.DEFAULT_SEPARATOR, charset = IdGenerator.DEFAULT_CHARSET, } = options;
|
|
67
67
|
const randomPart = this.generateRandomString(length, charset);
|
|
68
|
-
return prefix
|
|
69
|
-
? `${prefix}${separator}${randomPart}`
|
|
70
|
-
: randomPart;
|
|
68
|
+
return prefix ? `${prefix}${separator}${randomPart}` : randomPart;
|
|
71
69
|
}
|
|
72
70
|
/**
|
|
73
71
|
* Generates a custom ID for an entity with format PREFIX_XXXXXX
|
|
@@ -92,7 +90,7 @@ export class IdGenerator {
|
|
|
92
90
|
*/
|
|
93
91
|
isValid(id, entityType, options = {}) {
|
|
94
92
|
const prefix = this.entityPrefixes[entityType];
|
|
95
|
-
const { length = IdGenerator.DEFAULT_LENGTH, separator = IdGenerator.DEFAULT_SEPARATOR } = options;
|
|
93
|
+
const { length = IdGenerator.DEFAULT_LENGTH, separator = IdGenerator.DEFAULT_SEPARATOR, } = options;
|
|
96
94
|
if (!prefix) {
|
|
97
95
|
return false;
|
|
98
96
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export * from
|
|
2
|
-
export * from
|
|
3
|
-
export * from
|
|
1
|
+
export * from "./sanitization.js";
|
|
2
|
+
export * from "./rateLimiter.js";
|
|
3
|
+
export * from "./idGenerator.js";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { BaseErrorCode, McpError } from
|
|
1
|
+
import { BaseErrorCode, McpError } from "../../types-global/errors.js";
|
|
2
2
|
// Import config and utils
|
|
3
|
-
import { environment } from
|
|
4
|
-
import { logger } from
|
|
3
|
+
import { environment } from "../../config/index.js"; // Import environment from config
|
|
4
|
+
import { logger } from "../index.js";
|
|
5
5
|
/**
|
|
6
6
|
* Generic rate limiter that can be used across the application
|
|
7
7
|
*/
|
|
@@ -15,9 +15,9 @@ export class RateLimiter {
|
|
|
15
15
|
static DEFAULT_CONFIG = {
|
|
16
16
|
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
17
17
|
maxRequests: 100, // 100 requests per window
|
|
18
|
-
errorMessage:
|
|
18
|
+
errorMessage: "Rate limit exceeded. Please try again in {waitTime} seconds.",
|
|
19
19
|
skipInDevelopment: false,
|
|
20
|
-
cleanupInterval: 5 * 60 * 1000 // 5 minutes
|
|
20
|
+
cleanupInterval: 5 * 60 * 1000, // 5 minutes
|
|
21
21
|
};
|
|
22
22
|
/**
|
|
23
23
|
* Create a new rate limiter
|
|
@@ -63,7 +63,7 @@ export class RateLimiter {
|
|
|
63
63
|
}
|
|
64
64
|
if (expiredCount > 0) {
|
|
65
65
|
logger.debug(`Cleaned up ${expiredCount} expired rate limit entries`, {
|
|
66
|
-
totalRemaining: this.limits.size
|
|
66
|
+
totalRemaining: this.limits.size,
|
|
67
67
|
});
|
|
68
68
|
}
|
|
69
69
|
}
|
|
@@ -90,7 +90,7 @@ export class RateLimiter {
|
|
|
90
90
|
*/
|
|
91
91
|
reset() {
|
|
92
92
|
this.limits.clear();
|
|
93
|
-
logger.debug(
|
|
93
|
+
logger.debug("Rate limiter reset, all limits cleared");
|
|
94
94
|
}
|
|
95
95
|
/**
|
|
96
96
|
* Check if a request exceeds the rate limit
|
|
@@ -100,7 +100,7 @@ export class RateLimiter {
|
|
|
100
100
|
*/
|
|
101
101
|
check(key, context) {
|
|
102
102
|
// Skip in development if configured, using the validated environment from config
|
|
103
|
-
if (this.config.skipInDevelopment && environment ===
|
|
103
|
+
if (this.config.skipInDevelopment && environment === "development") {
|
|
104
104
|
return;
|
|
105
105
|
}
|
|
106
106
|
// Generate key using custom generator if provided
|
|
@@ -117,7 +117,7 @@ export class RateLimiter {
|
|
|
117
117
|
if (!entry || now >= entry.resetTime) {
|
|
118
118
|
const newEntry = {
|
|
119
119
|
count: 1,
|
|
120
|
-
resetTime: now + this.config.windowMs
|
|
120
|
+
resetTime: now + this.config.windowMs,
|
|
121
121
|
};
|
|
122
122
|
this.limits.set(limitKey, newEntry);
|
|
123
123
|
return newEntry;
|
|
@@ -125,9 +125,11 @@ export class RateLimiter {
|
|
|
125
125
|
// Check if limit exceeded
|
|
126
126
|
if (entry.count >= this.config.maxRequests) {
|
|
127
127
|
const waitTime = Math.ceil((entry.resetTime - now) / 1000);
|
|
128
|
-
const errorMessage = this.config.errorMessage?.replace(
|
|
129
|
-
|
|
130
|
-
|
|
128
|
+
const errorMessage = this.config.errorMessage?.replace("{waitTime}", waitTime.toString()) || `Rate limit exceeded. Please try again in ${waitTime} seconds.`;
|
|
129
|
+
throw new McpError(BaseErrorCode.RATE_LIMITED, errorMessage, {
|
|
130
|
+
waitTime,
|
|
131
|
+
key: limitKey,
|
|
132
|
+
});
|
|
131
133
|
}
|
|
132
134
|
// Increment counter and return updated entry
|
|
133
135
|
entry.count++;
|
|
@@ -150,7 +152,7 @@ export class RateLimiter {
|
|
|
150
152
|
current: entry.count,
|
|
151
153
|
limit: this.config.maxRequests,
|
|
152
154
|
remaining: Math.max(0, this.config.maxRequests - entry.count),
|
|
153
|
-
resetTime: entry.resetTime
|
|
155
|
+
resetTime: entry.resetTime,
|
|
154
156
|
};
|
|
155
157
|
}
|
|
156
158
|
/**
|
|
@@ -170,5 +172,5 @@ export class RateLimiter {
|
|
|
170
172
|
*/
|
|
171
173
|
export const rateLimiter = new RateLimiter({
|
|
172
174
|
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
173
|
-
maxRequests: 100 // 100 requests per window
|
|
175
|
+
maxRequests: 100, // 100 requests per window
|
|
174
176
|
});
|