@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.
Files changed (97) hide show
  1. package/README.md +8 -11
  2. package/dist/config/index.js +7 -7
  3. package/dist/index.js +35 -21
  4. package/dist/mcp-server/server.js +72 -56
  5. package/dist/mcp-server/tools/gitAdd/index.js +1 -1
  6. package/dist/mcp-server/tools/gitAdd/logic.js +88 -39
  7. package/dist/mcp-server/tools/gitAdd/registration.js +17 -14
  8. package/dist/mcp-server/tools/gitBranch/index.js +1 -1
  9. package/dist/mcp-server/tools/gitBranch/logic.js +213 -85
  10. package/dist/mcp-server/tools/gitBranch/registration.js +16 -13
  11. package/dist/mcp-server/tools/gitCheckout/index.js +1 -1
  12. package/dist/mcp-server/tools/gitCheckout/logic.js +85 -145
  13. package/dist/mcp-server/tools/gitCheckout/registration.js +16 -14
  14. package/dist/mcp-server/tools/gitCherryPick/index.js +1 -1
  15. package/dist/mcp-server/tools/gitCherryPick/logic.js +100 -41
  16. package/dist/mcp-server/tools/gitCherryPick/registration.js +21 -14
  17. package/dist/mcp-server/tools/gitClean/index.js +1 -1
  18. package/dist/mcp-server/tools/gitClean/logic.js +93 -41
  19. package/dist/mcp-server/tools/gitClean/registration.js +19 -16
  20. package/dist/mcp-server/tools/gitClearWorkingDir/index.js +1 -1
  21. package/dist/mcp-server/tools/gitClearWorkingDir/logic.js +14 -11
  22. package/dist/mcp-server/tools/gitClearWorkingDir/registration.js +19 -13
  23. package/dist/mcp-server/tools/gitClone/index.js +1 -1
  24. package/dist/mcp-server/tools/gitClone/logic.js +89 -30
  25. package/dist/mcp-server/tools/gitClone/registration.js +15 -12
  26. package/dist/mcp-server/tools/gitCommit/index.js +1 -1
  27. package/dist/mcp-server/tools/gitCommit/logic.js +198 -76
  28. package/dist/mcp-server/tools/gitCommit/registration.js +23 -20
  29. package/dist/mcp-server/tools/gitDiff/index.js +1 -1
  30. package/dist/mcp-server/tools/gitDiff/logic.js +124 -44
  31. package/dist/mcp-server/tools/gitDiff/registration.js +16 -14
  32. package/dist/mcp-server/tools/gitFetch/index.js +1 -1
  33. package/dist/mcp-server/tools/gitFetch/logic.js +78 -49
  34. package/dist/mcp-server/tools/gitFetch/registration.js +16 -14
  35. package/dist/mcp-server/tools/gitInit/index.js +1 -1
  36. package/dist/mcp-server/tools/gitInit/logic.js +88 -34
  37. package/dist/mcp-server/tools/gitInit/registration.js +32 -18
  38. package/dist/mcp-server/tools/gitLog/index.js +1 -1
  39. package/dist/mcp-server/tools/gitLog/logic.js +133 -47
  40. package/dist/mcp-server/tools/gitLog/registration.js +16 -14
  41. package/dist/mcp-server/tools/gitMerge/index.js +1 -1
  42. package/dist/mcp-server/tools/gitMerge/logic.js +102 -61
  43. package/dist/mcp-server/tools/gitMerge/registration.js +17 -14
  44. package/dist/mcp-server/tools/gitPull/index.js +1 -1
  45. package/dist/mcp-server/tools/gitPull/logic.js +90 -69
  46. package/dist/mcp-server/tools/gitPull/registration.js +16 -14
  47. package/dist/mcp-server/tools/gitPush/index.js +1 -1
  48. package/dist/mcp-server/tools/gitPush/logic.js +116 -100
  49. package/dist/mcp-server/tools/gitPush/registration.js +16 -14
  50. package/dist/mcp-server/tools/gitRebase/index.js +1 -1
  51. package/dist/mcp-server/tools/gitRebase/logic.js +121 -82
  52. package/dist/mcp-server/tools/gitRebase/registration.js +21 -14
  53. package/dist/mcp-server/tools/gitRemote/index.js +1 -1
  54. package/dist/mcp-server/tools/gitRemote/logic.js +108 -52
  55. package/dist/mcp-server/tools/gitRemote/registration.js +14 -11
  56. package/dist/mcp-server/tools/gitReset/index.js +1 -1
  57. package/dist/mcp-server/tools/gitReset/logic.js +65 -37
  58. package/dist/mcp-server/tools/gitReset/registration.js +14 -12
  59. package/dist/mcp-server/tools/gitSetWorkingDir/index.js +1 -1
  60. package/dist/mcp-server/tools/gitSetWorkingDir/logic.js +74 -34
  61. package/dist/mcp-server/tools/gitSetWorkingDir/registration.js +18 -11
  62. package/dist/mcp-server/tools/gitShow/index.js +1 -1
  63. package/dist/mcp-server/tools/gitShow/logic.js +78 -35
  64. package/dist/mcp-server/tools/gitShow/registration.js +17 -12
  65. package/dist/mcp-server/tools/gitStash/index.js +1 -1
  66. package/dist/mcp-server/tools/gitStash/logic.js +143 -58
  67. package/dist/mcp-server/tools/gitStash/registration.js +19 -12
  68. package/dist/mcp-server/tools/gitStatus/index.js +1 -1
  69. package/dist/mcp-server/tools/gitStatus/logic.js +100 -58
  70. package/dist/mcp-server/tools/gitStatus/registration.js +15 -12
  71. package/dist/mcp-server/tools/gitTag/index.js +1 -1
  72. package/dist/mcp-server/tools/gitTag/logic.js +124 -51
  73. package/dist/mcp-server/tools/gitTag/registration.js +14 -11
  74. package/dist/mcp-server/tools/gitWorktree/index.js +1 -1
  75. package/dist/mcp-server/tools/gitWorktree/logic.js +204 -95
  76. package/dist/mcp-server/tools/gitWorktree/registration.js +14 -11
  77. package/dist/mcp-server/tools/gitWrapupInstructions/index.js +1 -1
  78. package/dist/mcp-server/tools/gitWrapupInstructions/logic.js +23 -11
  79. package/dist/mcp-server/tools/gitWrapupInstructions/registration.js +14 -12
  80. package/dist/mcp-server/transports/httpTransport.js +187 -79
  81. package/dist/mcp-server/transports/stdioTransport.js +14 -8
  82. package/dist/types-global/errors.js +9 -4
  83. package/dist/utils/index.js +4 -4
  84. package/dist/utils/internal/errorHandler.js +62 -40
  85. package/dist/utils/internal/index.js +3 -3
  86. package/dist/utils/internal/logger.js +97 -54
  87. package/dist/utils/internal/requestContext.js +7 -5
  88. package/dist/utils/metrics/index.js +1 -1
  89. package/dist/utils/metrics/tokenCounter.js +18 -14
  90. package/dist/utils/parsing/dateParser.js +5 -5
  91. package/dist/utils/parsing/index.js +2 -2
  92. package/dist/utils/parsing/jsonParser.js +20 -11
  93. package/dist/utils/security/idGenerator.js +8 -10
  94. package/dist/utils/security/index.js +3 -3
  95. package/dist/utils/security/rateLimiter.js +16 -14
  96. package/dist/utils/security/sanitization.js +139 -82
  97. package/package.json +45 -23
@@ -1,20 +1,25 @@
1
- import { exec } from 'child_process';
2
- import { promisify } from 'util';
3
- import { z } from 'zod';
1
+ import { exec } from "child_process";
2
+ import { promisify } from "util";
3
+ import { z } from "zod";
4
4
  // Import utils from barrel (logger from ../utils/internal/logger.js)
5
- import { logger } from '../../../utils/index.js';
5
+ import { logger } from "../../../utils/index.js";
6
6
  // Import utils from barrel (RequestContext from ../utils/internal/requestContext.js)
7
- import { BaseErrorCode, McpError } from '../../../types-global/errors.js'; // Keep direct import for types-global
7
+ import { BaseErrorCode, McpError } from "../../../types-global/errors.js"; // Keep direct import for types-global
8
8
  // Import utils from barrel (sanitization from ../utils/security/sanitization.js)
9
- import { sanitization } from '../../../utils/index.js';
9
+ import { sanitization } from "../../../utils/index.js";
10
10
  const execAsync = promisify(exec);
11
11
  // Define the input schema for the git_add tool using Zod
12
12
  export const GitAddInputSchema = z.object({
13
- path: z.string().min(1).optional().default('.').describe("Path to the Git repository. Defaults to the directory set via `git_set_working_dir` for the session; set 'git_set_working_dir' if not set."),
14
- files: z.union([
15
- z.string().min(1),
16
- z.array(z.string().min(1))
17
- ]).default('.').describe('Files or patterns to stage, defaults to all changes (\'.\')'),
13
+ path: z
14
+ .string()
15
+ .min(1)
16
+ .optional()
17
+ .default(".")
18
+ .describe("Path to the Git repository. Defaults to the directory set via `git_set_working_dir` for the session; set 'git_set_working_dir' if not set."),
19
+ files: z
20
+ .union([z.string().min(1), z.array(z.string().min(1))])
21
+ .default(".")
22
+ .describe("Files or patterns to stage, defaults to all changes ('.')"),
18
23
  });
19
24
  /**
20
25
  * Executes the 'git add' command and returns structured JSON output.
@@ -24,16 +29,18 @@ export const GitAddInputSchema = z.object({
24
29
  * @returns {Promise<GitAddResult>} A promise that resolves with the structured add result.
25
30
  * @throws {McpError} Throws an McpError if path resolution or validation fails, or if the git command fails unexpectedly.
26
31
  */
27
- export async function addGitFiles(input, context // Add getter to context
28
- ) {
29
- const operation = 'addGitFiles';
32
+ export async function addGitFiles(input, context) {
33
+ const operation = "addGitFiles";
30
34
  logger.debug(`Executing ${operation}`, { ...context, input });
31
35
  let targetPath;
32
36
  try {
33
37
  // Resolve the target path
34
- if (input.path && input.path !== '.') {
38
+ if (input.path && input.path !== ".") {
35
39
  targetPath = input.path;
36
- logger.debug(`Using provided path: ${targetPath}`, { ...context, operation });
40
+ logger.debug(`Using provided path: ${targetPath}`, {
41
+ ...context,
42
+ operation,
43
+ });
37
44
  }
38
45
  else {
39
46
  const workingDir = context.getWorkingDirectory();
@@ -41,15 +48,29 @@ export async function addGitFiles(input, context // Add getter to context
41
48
  throw new McpError(BaseErrorCode.VALIDATION_ERROR, "No path provided and no working directory set for the session.", { context, operation });
42
49
  }
43
50
  targetPath = workingDir;
44
- logger.debug(`Using session working directory: ${targetPath}`, { ...context, operation, sessionId: context.sessionId });
51
+ logger.debug(`Using session working directory: ${targetPath}`, {
52
+ ...context,
53
+ operation,
54
+ sessionId: context.sessionId,
55
+ });
45
56
  }
46
57
  // Sanitize the resolved path
47
- const sanitizedPathInfo = sanitization.sanitizePath(targetPath, { allowAbsolute: true });
48
- logger.debug('Sanitized repository path', { ...context, operation, sanitizedPathInfo });
58
+ const sanitizedPathInfo = sanitization.sanitizePath(targetPath, {
59
+ allowAbsolute: true,
60
+ });
61
+ logger.debug("Sanitized repository path", {
62
+ ...context,
63
+ operation,
64
+ sanitizedPathInfo,
65
+ });
49
66
  targetPath = sanitizedPathInfo.sanitizedPath; // Use the sanitized path going forward
50
67
  }
51
68
  catch (error) {
52
- logger.error('Path resolution or sanitization failed', { ...context, operation, error });
69
+ logger.error("Path resolution or sanitization failed", {
70
+ ...context,
71
+ operation,
72
+ error,
73
+ });
53
74
  if (error instanceof McpError) {
54
75
  throw error;
55
76
  }
@@ -61,30 +82,40 @@ export async function addGitFiles(input, context // Add getter to context
61
82
  try {
62
83
  if (Array.isArray(filesToStage)) {
63
84
  if (filesToStage.length === 0) {
64
- logger.warning('Empty array provided for files, defaulting to staging all changes.', { ...context, operation });
65
- filesArg = '.'; // Default to staging all if array is empty
85
+ logger.warning("Empty array provided for files, defaulting to staging all changes.", { ...context, operation });
86
+ filesArg = "."; // Default to staging all if array is empty
66
87
  }
67
88
  else {
68
89
  // Quote each file path individually
69
- filesArg = filesToStage.map(file => {
70
- const sanitizedFile = file.startsWith('-') ? `./${file}` : file; // Prefix with './' if it starts with a dash
90
+ filesArg = filesToStage
91
+ .map((file) => {
92
+ const sanitizedFile = file.startsWith("-") ? `./${file}` : file; // Prefix with './' if it starts with a dash
71
93
  return `"${sanitizedFile.replace(/"/g, '\\"')}"`; // Escape quotes within path
72
- }).join(' ');
94
+ })
95
+ .join(" ");
73
96
  }
74
97
  }
75
- else { // Single string case
76
- const sanitizedFile = filesToStage.startsWith('-') ? `./${filesToStage}` : filesToStage; // Prefix with './' if it starts with a dash
98
+ else {
99
+ // Single string case
100
+ const sanitizedFile = filesToStage.startsWith("-")
101
+ ? `./${filesToStage}`
102
+ : filesToStage; // Prefix with './' if it starts with a dash
77
103
  filesArg = `"${sanitizedFile.replace(/"/g, '\\"')}"`;
78
104
  }
79
105
  }
80
106
  catch (err) {
81
- logger.error('File path validation/quoting failed', { ...context, operation, files: filesToStage, error: err });
107
+ logger.error("File path validation/quoting failed", {
108
+ ...context,
109
+ operation,
110
+ files: filesToStage,
111
+ error: err,
112
+ });
82
113
  throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Invalid file path/pattern provided: ${err instanceof Error ? err.message : String(err)}`, { context, operation, originalError: err });
83
114
  }
84
115
  // This check should ideally not be needed now due to the logic above
85
116
  if (!filesArg) {
86
- logger.error('Internal error: filesArg is unexpectedly empty after processing.', { ...context, operation });
87
- throw new McpError(BaseErrorCode.INTERNAL_ERROR, 'Internal error preparing git add command.', { context, operation });
117
+ logger.error("Internal error: filesArg is unexpectedly empty after processing.", { ...context, operation });
118
+ throw new McpError(BaseErrorCode.INTERNAL_ERROR, "Internal error preparing git add command.", { context, operation });
88
119
  }
89
120
  try {
90
121
  // Use the resolved targetPath
@@ -93,25 +124,43 @@ export async function addGitFiles(input, context // Add getter to context
93
124
  const { stdout, stderr } = await execAsync(command);
94
125
  if (stderr) {
95
126
  // Log stderr as warning, as 'git add' can produce warnings but still succeed.
96
- logger.warning(`Git add command produced stderr`, { ...context, operation, stderr });
127
+ logger.warning(`Git add command produced stderr`, {
128
+ ...context,
129
+ operation,
130
+ stderr,
131
+ });
97
132
  }
98
- logger.info(`${operation} executed successfully`, { ...context, operation, path: targetPath, files: filesToStage });
99
- const filesAddedDesc = Array.isArray(filesToStage) ? filesToStage.join(', ') : filesToStage;
133
+ const filesAddedDesc = Array.isArray(filesToStage)
134
+ ? filesToStage.join(", ")
135
+ : filesToStage;
136
+ const successMessage = `Successfully staged: ${filesAddedDesc}`;
137
+ logger.info(successMessage, {
138
+ ...context,
139
+ operation,
140
+ path: targetPath,
141
+ files: filesToStage,
142
+ });
100
143
  const reminder = "Remember to write clear, concise commit messages using the Conventional Commits format (e.g., 'feat(scope): subject').";
101
144
  // Use statusMessage and add reminder
102
145
  return {
103
146
  success: true,
104
- statusMessage: `Successfully staged: ${filesAddedDesc}. ${reminder}`,
105
- filesStaged: filesToStage
147
+ statusMessage: `${successMessage}. ${reminder}`,
148
+ filesStaged: filesToStage,
106
149
  };
107
150
  }
108
151
  catch (error) {
109
- logger.error(`Failed to execute git add command`, { ...context, operation, path: targetPath, error: error.message, stderr: error.stderr });
110
- const errorMessage = error.stderr || error.message || '';
111
- if (errorMessage.toLowerCase().includes('not a git repository')) {
152
+ logger.error(`Failed to execute git add command`, {
153
+ ...context,
154
+ operation,
155
+ path: targetPath,
156
+ error: error.message,
157
+ stderr: error.stderr,
158
+ });
159
+ const errorMessage = error.stderr || error.message || "";
160
+ if (errorMessage.toLowerCase().includes("not a git repository")) {
112
161
  throw new McpError(BaseErrorCode.NOT_FOUND, `Path is not a Git repository: ${targetPath}`, { context, operation, originalError: error });
113
162
  }
114
- if (errorMessage.toLowerCase().includes('did not match any files')) {
163
+ if (errorMessage.toLowerCase().includes("did not match any files")) {
115
164
  // Still throw an error, but return structured info in the catch block of the registration
116
165
  throw new McpError(BaseErrorCode.NOT_FOUND, `Specified files/patterns did not match any files in ${targetPath}: ${filesArg}`, { context, operation, originalError: error, filesStaged: filesToStage });
117
166
  }
@@ -1,12 +1,12 @@
1
1
  // Import utils from barrel (logger from ../utils/internal/logger.js)
2
- import { logger } from '../../../utils/index.js';
2
+ import { logger } from "../../../utils/index.js";
3
3
  // Import utils from barrel (ErrorHandler from ../utils/internal/errorHandler.js)
4
- import { ErrorHandler } from '../../../utils/index.js';
4
+ import { ErrorHandler } from "../../../utils/index.js";
5
5
  // Import utils from barrel (requestContextService from ../utils/internal/requestContext.js)
6
- import { requestContextService } from '../../../utils/index.js';
6
+ import { requestContextService } from "../../../utils/index.js";
7
7
  // Import the result type along with the function and input schema
8
- import { BaseErrorCode } from '../../../types-global/errors.js'; // Import BaseErrorCode
9
- import { addGitFiles, GitAddInputSchema } from './logic.js';
8
+ import { BaseErrorCode } from "../../../types-global/errors.js"; // Import BaseErrorCode
9
+ import { addGitFiles, GitAddInputSchema, } from "./logic.js";
10
10
  let _getWorkingDirectory;
11
11
  let _getSessionId;
12
12
  /**
@@ -18,10 +18,10 @@ let _getSessionId;
18
18
  export function initializeGitAddStateAccessors(getWdFn, getSidFn) {
19
19
  _getWorkingDirectory = getWdFn;
20
20
  _getSessionId = getSidFn;
21
- logger.info('State accessors initialized for git_add tool registration.');
21
+ logger.info("State accessors initialized for git_add tool registration.");
22
22
  }
23
- const TOOL_NAME = 'git_add';
24
- const TOOL_DESCRIPTION = 'Stages changes in the Git repository for the next commit by adding file contents to the index (staging area). Can stage specific files/patterns or all changes (default: \'.\'). Returns the result as a JSON object.';
23
+ const TOOL_NAME = "git_add";
24
+ const TOOL_DESCRIPTION = "Stages changes in the Git repository for the next commit by adding file contents to the index (staging area). Can stage specific files/patterns or all changes (default: '.'). Returns the result as a JSON object.";
25
25
  /**
26
26
  * Registers the git_add tool with the MCP server.
27
27
  * Uses the high-level server.tool() method for registration, schema validation, and routing.
@@ -32,15 +32,18 @@ const TOOL_DESCRIPTION = 'Stages changes in the Git repository for the next comm
32
32
  */
33
33
  export const registerGitAddTool = async (server) => {
34
34
  if (!_getWorkingDirectory || !_getSessionId) {
35
- throw new Error('State accessors for git_add must be initialized before registration.');
35
+ throw new Error("State accessors for git_add must be initialized before registration.");
36
36
  }
37
- const operation = 'registerGitAddTool';
37
+ const operation = "registerGitAddTool";
38
38
  const context = requestContextService.createRequestContext({ operation });
39
39
  await ErrorHandler.tryCatch(async () => {
40
40
  server.tool(TOOL_NAME, TOOL_DESCRIPTION, GitAddInputSchema.shape, // Provide the Zod schema shape
41
41
  async (validatedArgs, callContext) => {
42
- const toolOperation = 'tool:git_add';
43
- const requestContext = requestContextService.createRequestContext({ operation: toolOperation, parentContext: callContext });
42
+ const toolOperation = "tool:git_add";
43
+ const requestContext = requestContextService.createRequestContext({
44
+ operation: toolOperation,
45
+ parentContext: callContext,
46
+ });
44
47
  const sessionId = _getSessionId(requestContext);
45
48
  const getWorkingDirectoryForSession = () => {
46
49
  return _getWorkingDirectory(sessionId);
@@ -56,10 +59,10 @@ export const registerGitAddTool = async (server) => {
56
59
  const addResult = await addGitFiles(validatedArgs, logicContext);
57
60
  // Format the successful result as a JSON string within TextContent
58
61
  const resultContent = {
59
- type: 'text',
62
+ type: "text",
60
63
  // Stringify the JSON object for the response content
61
64
  text: JSON.stringify(addResult, null, 2), // Pretty-print JSON
62
- contentType: 'application/json',
65
+ contentType: "application/json",
63
66
  };
64
67
  logger.info(`Tool ${TOOL_NAME} executed successfully, returning JSON`, logicContext);
65
68
  return { content: [resultContent] };
@@ -2,6 +2,6 @@
2
2
  * @fileoverview Barrel file for the git_branch tool.
3
3
  * Exports the registration function and state accessor initialization function.
4
4
  */
5
- export { registerGitBranchTool, initializeGitBranchStateAccessors } from './registration.js';
5
+ export { registerGitBranchTool, initializeGitBranchStateAccessors, } from "./registration.js";
6
6
  // Export types if needed elsewhere, e.g.:
7
7
  // export type { GitBranchInput, GitBranchResult } from './logic.js';