@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,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
|
});
|