@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,31 +1,64 @@
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 BASE input schema for the git_branch tool using Zod
12
12
  export const GitBranchBaseSchema = z.object({
13
- path: z.string().min(1).optional().default('.').describe("Path to the local Git repository. Defaults to the directory set via `git_set_working_dir` for the session; set 'git_set_working_dir' if not set."),
14
- mode: z.enum(['list', 'create', 'delete', 'rename', 'show-current']).describe("The branch operation to perform: 'list', 'create', 'delete', 'rename', 'show-current'."),
15
- branchName: z.string().min(1).optional().describe("The name of the branch (e.g., 'feat/new-login', 'main'). Required for 'create', 'delete', 'rename' modes."),
16
- newBranchName: z.string().min(1).optional().describe("The new name for the branch (e.g., 'fix/typo-in-readme'). Required for 'rename' mode."),
17
- startPoint: z.string().min(1).optional().describe("Optional commit hash, tag, or existing branch name (e.g., 'main', 'v1.0.0', 'commit-hash') to start the new branch from. Used only in 'create' mode. Defaults to HEAD."),
18
- force: z.boolean().default(false).describe("Force the operation. Use -D for delete, -M for rename, -f for create (if branch exists). Use with caution, as forcing operations can lead to data loss."),
19
- all: z.boolean().default(false).describe("List both local and remote-tracking branches. Used only in 'list' mode."),
20
- remote: z.boolean().default(false).describe("Act on remote-tracking branches. Used with 'list' (-r) or 'delete' (-r)."),
13
+ path: z
14
+ .string()
15
+ .min(1)
16
+ .optional()
17
+ .default(".")
18
+ .describe("Path to the local Git repository. Defaults to the directory set via `git_set_working_dir` for the session; set 'git_set_working_dir' if not set."),
19
+ mode: z
20
+ .enum(["list", "create", "delete", "rename", "show-current"])
21
+ .describe("The branch operation to perform: 'list', 'create', 'delete', 'rename', 'show-current'."),
22
+ branchName: z
23
+ .string()
24
+ .min(1)
25
+ .optional()
26
+ .describe("The name of the branch (e.g., 'feat/new-login', 'main'). Required for 'create', 'delete', 'rename' modes."),
27
+ newBranchName: z
28
+ .string()
29
+ .min(1)
30
+ .optional()
31
+ .describe("The new name for the branch (e.g., 'fix/typo-in-readme'). Required for 'rename' mode."),
32
+ startPoint: z
33
+ .string()
34
+ .min(1)
35
+ .optional()
36
+ .describe("Optional commit hash, tag, or existing branch name (e.g., 'main', 'v1.0.0', 'commit-hash') to start the new branch from. Used only in 'create' mode. Defaults to HEAD."),
37
+ force: z
38
+ .boolean()
39
+ .default(false)
40
+ .describe("Force the operation. Use -D for delete, -M for rename, -f for create (if branch exists). Use with caution, as forcing operations can lead to data loss."),
41
+ all: z
42
+ .boolean()
43
+ .default(false)
44
+ .describe("List both local and remote-tracking branches. Used only in 'list' mode."),
45
+ remote: z
46
+ .boolean()
47
+ .default(false)
48
+ .describe("Act on remote-tracking branches. Used with 'list' (-r) or 'delete' (-r)."),
21
49
  });
22
50
  // Apply refinements and export the FINAL schema for validation within the handler
23
- export const GitBranchInputSchema = GitBranchBaseSchema.refine(data => !(data.mode === 'create' && !data.branchName), {
24
- message: "A 'branchName' is required for 'create' mode.", path: ["branchName"],
25
- }).refine(data => !(data.mode === 'delete' && !data.branchName), {
26
- message: "A 'branchName' is required for 'delete' mode.", path: ["branchName"],
27
- }).refine(data => !(data.mode === 'rename' && (!data.branchName || !data.newBranchName)), {
28
- message: "Both 'branchName' (old name) and 'newBranchName' are required for 'rename' mode.", path: ["branchName", "newBranchName"],
51
+ export const GitBranchInputSchema = GitBranchBaseSchema.refine((data) => !(data.mode === "create" && !data.branchName), {
52
+ message: "A 'branchName' is required for 'create' mode.",
53
+ path: ["branchName"],
54
+ })
55
+ .refine((data) => !(data.mode === "delete" && !data.branchName), {
56
+ message: "A 'branchName' is required for 'delete' mode.",
57
+ path: ["branchName"],
58
+ })
59
+ .refine((data) => !(data.mode === "rename" && (!data.branchName || !data.newBranchName)), {
60
+ message: "Both 'branchName' (old name) and 'newBranchName' are required for 'rename' mode.",
61
+ path: ["branchName", "newBranchName"],
29
62
  });
30
63
  /**
31
64
  * Executes git branch commands based on the specified mode.
@@ -42,25 +75,41 @@ export async function gitBranchLogic(input, context) {
42
75
  try {
43
76
  // Resolve and sanitize the target path
44
77
  const workingDir = context.getWorkingDirectory();
45
- targetPath = (input.path && input.path !== '.')
46
- ? input.path
47
- : workingDir ?? '.';
48
- if (targetPath === '.' && !workingDir) {
78
+ targetPath =
79
+ input.path && input.path !== "." ? input.path : (workingDir ?? ".");
80
+ if (targetPath === "." && !workingDir) {
49
81
  logger.warning("Executing git branch in server's CWD as no path provided and no session WD set.", { ...context, operation });
50
82
  targetPath = process.cwd();
51
83
  }
52
- else if (targetPath === '.' && workingDir) {
84
+ else if (targetPath === "." && workingDir) {
53
85
  targetPath = workingDir;
54
- logger.debug(`Using session working directory: ${targetPath}`, { ...context, operation, sessionId: context.sessionId });
86
+ logger.debug(`Using session working directory: ${targetPath}`, {
87
+ ...context,
88
+ operation,
89
+ sessionId: context.sessionId,
90
+ });
55
91
  }
56
92
  else {
57
- logger.debug(`Using provided path: ${targetPath}`, { ...context, operation });
93
+ logger.debug(`Using provided path: ${targetPath}`, {
94
+ ...context,
95
+ operation,
96
+ });
58
97
  }
59
- targetPath = sanitization.sanitizePath(targetPath, { allowAbsolute: true }).sanitizedPath;
60
- logger.debug('Sanitized path', { ...context, operation, sanitizedPath: targetPath });
98
+ targetPath = sanitization.sanitizePath(targetPath, {
99
+ allowAbsolute: true,
100
+ }).sanitizedPath;
101
+ logger.debug("Sanitized path", {
102
+ ...context,
103
+ operation,
104
+ sanitizedPath: targetPath,
105
+ });
61
106
  }
62
107
  catch (error) {
63
- logger.error('Path resolution or sanitization failed', { ...context, operation, error });
108
+ logger.error("Path resolution or sanitization failed", {
109
+ ...context,
110
+ operation,
111
+ error,
112
+ });
64
113
  if (error instanceof McpError)
65
114
  throw error;
66
115
  throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Invalid path: ${error instanceof Error ? error.message : String(error)}`, { context, operation, originalError: error });
@@ -69,82 +118,129 @@ export async function gitBranchLogic(input, context) {
69
118
  let command;
70
119
  let result;
71
120
  switch (input.mode) {
72
- case 'list':
121
+ case "list":
73
122
  command = `git -C "${targetPath}" branch --list --no-color`; // Start with basic list
74
123
  if (input.all)
75
- command += ' -a'; // Add -a if requested
124
+ command += " -a"; // Add -a if requested
76
125
  else if (input.remote)
77
- command += ' -r'; // Add -r if requested (exclusive with -a)
78
- command += ' --verbose'; // Add verbose for commit info
79
- logger.debug(`Executing command: ${command}`, { ...context, operation });
126
+ command += " -r"; // Add -r if requested (exclusive with -a)
127
+ command += " --verbose"; // Add verbose for commit info
128
+ logger.debug(`Executing command: ${command}`, {
129
+ ...context,
130
+ operation,
131
+ });
80
132
  const { stdout: listStdout } = await execAsync(command);
81
- const branches = listStdout.trim().split('\n')
82
- .filter(line => line && !line.match(/^\s*->\s*/)) // Filter out HEAD pointer lines if any
83
- .map(line => {
84
- const isCurrent = line.startsWith('* ');
85
- const trimmedLine = line.replace(/^\*?\s+/, ''); // Remove leading '*' and spaces
133
+ const branches = listStdout
134
+ .trim()
135
+ .split("\n")
136
+ .filter((line) => line && !line.match(/^\s*->\s*/)) // Filter out HEAD pointer lines if any
137
+ .map((line) => {
138
+ const isCurrent = line.startsWith("* ");
139
+ const trimmedLine = line.replace(/^\*?\s+/, ""); // Remove leading '*' and spaces
86
140
  // Determine isRemote based on the raw trimmed line BEFORE splitting
87
- const isRemote = trimmedLine.startsWith('remotes/');
141
+ const isRemote = trimmedLine.startsWith("remotes/");
88
142
  const parts = trimmedLine.split(/\s+/);
89
143
  const name = parts[0]; // This might be 'remotes/origin/main' or just 'main'
90
144
  const commitHash = parts[1] || undefined; // Verbose gives hash
91
- const commitSubject = parts.slice(2).join(' ') || undefined; // Verbose gives subject
145
+ const commitSubject = parts.slice(2).join(" ") || undefined; // Verbose gives subject
92
146
  // Return the correct name (without 'remotes/' prefix if it was remote) and the isRemote flag
93
147
  return {
94
- name: isRemote ? name.split('/').slice(2).join('/') : name, // e.g., 'origin/main' or 'main'
148
+ name: isRemote ? name.split("/").slice(2).join("/") : name, // e.g., 'origin/main' or 'main'
95
149
  isCurrent,
96
150
  isRemote, // Use the flag determined before splitting
97
151
  commitHash,
98
- commitSubject
152
+ commitSubject,
99
153
  };
100
154
  });
101
- const currentBranch = branches.find(b => b.isCurrent)?.name;
102
- result = { success: true, mode: 'list', branches, currentBranch };
155
+ const currentBranch = branches.find((b) => b.isCurrent)?.name;
156
+ result = { success: true, mode: "list", branches, currentBranch };
103
157
  break;
104
- case 'create':
158
+ case "create":
105
159
  // branchName is validated by Zod refine
106
160
  command = `git -C "${targetPath}" branch `;
107
161
  if (input.force)
108
- command += '-f ';
162
+ command += "-f ";
109
163
  command += `"${input.branchName}"`; // branchName is guaranteed by refine
110
164
  if (input.startPoint)
111
165
  command += ` "${input.startPoint}"`;
112
- logger.debug(`Executing command: ${command}`, { ...context, operation });
166
+ logger.debug(`Executing command: ${command}`, {
167
+ ...context,
168
+ operation,
169
+ });
113
170
  await execAsync(command);
114
- result = { success: true, mode: 'create', branchName: input.branchName, message: `Branch '${input.branchName}' created successfully.` };
171
+ result = {
172
+ success: true,
173
+ mode: "create",
174
+ branchName: input.branchName,
175
+ message: `Branch '${input.branchName}' created successfully.`,
176
+ };
115
177
  break;
116
- case 'delete':
178
+ case "delete":
117
179
  // branchName is validated by Zod refine
118
180
  command = `git -C "${targetPath}" branch `;
119
181
  if (input.remote)
120
- command += '-r ';
121
- command += input.force ? '-D ' : '-d ';
182
+ command += "-r ";
183
+ command += input.force ? "-D " : "-d ";
122
184
  command += `"${input.branchName}"`; // branchName is guaranteed by refine
123
- logger.debug(`Executing command: ${command}`, { ...context, operation });
185
+ logger.debug(`Executing command: ${command}`, {
186
+ ...context,
187
+ operation,
188
+ });
124
189
  const { stdout: deleteStdout } = await execAsync(command);
125
- result = { success: true, mode: 'delete', branchName: input.branchName, wasRemote: input.remote, message: deleteStdout.trim() || `Branch '${input.branchName}' deleted successfully.` };
190
+ result = {
191
+ success: true,
192
+ mode: "delete",
193
+ branchName: input.branchName,
194
+ wasRemote: input.remote,
195
+ message: deleteStdout.trim() ||
196
+ `Branch '${input.branchName}' deleted successfully.`,
197
+ };
126
198
  break;
127
- case 'rename':
199
+ case "rename":
128
200
  // branchName and newBranchName validated by Zod refine
129
201
  command = `git -C "${targetPath}" branch `;
130
- command += input.force ? '-M ' : '-m ';
202
+ command += input.force ? "-M " : "-m ";
131
203
  command += `"${input.branchName}" "${input.newBranchName}"`;
132
- logger.debug(`Executing command: ${command}`, { ...context, operation });
204
+ logger.debug(`Executing command: ${command}`, {
205
+ ...context,
206
+ operation,
207
+ });
133
208
  await execAsync(command);
134
- result = { success: true, mode: 'rename', oldBranchName: input.branchName, newBranchName: input.newBranchName, message: `Branch '${input.branchName}' renamed to '${input.newBranchName}' successfully.` };
209
+ result = {
210
+ success: true,
211
+ mode: "rename",
212
+ oldBranchName: input.branchName,
213
+ newBranchName: input.newBranchName,
214
+ message: `Branch '${input.branchName}' renamed to '${input.newBranchName}' successfully.`,
215
+ };
135
216
  break;
136
- case 'show-current':
217
+ case "show-current":
137
218
  command = `git -C "${targetPath}" branch --show-current`;
138
- logger.debug(`Executing command: ${command}`, { ...context, operation });
219
+ logger.debug(`Executing command: ${command}`, {
220
+ ...context,
221
+ operation,
222
+ });
139
223
  try {
140
224
  const { stdout: currentStdout } = await execAsync(command);
141
225
  const currentBranchName = currentStdout.trim();
142
- result = { success: true, mode: 'show-current', currentBranch: currentBranchName || null, message: currentBranchName ? `Current branch is '${currentBranchName}'.` : 'Currently in detached HEAD state.' };
226
+ result = {
227
+ success: true,
228
+ mode: "show-current",
229
+ currentBranch: currentBranchName || null,
230
+ message: currentBranchName
231
+ ? `Current branch is '${currentBranchName}'.`
232
+ : "Currently in detached HEAD state.",
233
+ };
143
234
  }
144
235
  catch (showError) {
145
236
  // Handle detached HEAD state specifically if command fails
146
- if (showError.stderr?.includes('HEAD detached')) {
147
- result = { success: true, mode: 'show-current', currentBranch: null, message: 'Currently in detached HEAD state.' };
237
+ if (showError.stderr?.includes("HEAD detached")) {
238
+ result = {
239
+ success: true,
240
+ mode: "show-current",
241
+ currentBranch: null,
242
+ message: "Currently in detached HEAD state.",
243
+ };
148
244
  }
149
245
  else {
150
246
  throw showError; // Re-throw other errors
@@ -155,37 +251,69 @@ export async function gitBranchLogic(input, context) {
155
251
  // Should not happen due to Zod validation
156
252
  throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Invalid mode: ${input.mode}`, { context, operation });
157
253
  }
158
- logger.info(`${operation} executed successfully`, { ...context, operation, path: targetPath });
254
+ logger.info(`git branch ${input.mode} executed successfully`, {
255
+ ...context,
256
+ operation,
257
+ path: targetPath,
258
+ result,
259
+ });
159
260
  return result;
160
261
  }
161
262
  catch (error) {
162
- const errorMessage = error.stderr || error.stdout || error.message || ''; // stdout might contain error messages too
163
- logger.error(`Failed to execute git branch command`, { ...context, operation, path: targetPath, error: errorMessage, stderr: error.stderr, stdout: error.stdout });
263
+ const errorMessage = error.stderr || error.stdout || error.message || ""; // stdout might contain error messages too
264
+ logger.error(`Failed to execute git branch command`, {
265
+ ...context,
266
+ operation,
267
+ path: targetPath,
268
+ error: errorMessage,
269
+ stderr: error.stderr,
270
+ stdout: error.stdout,
271
+ });
164
272
  // Specific error handling
165
- if (errorMessage.toLowerCase().includes('not a git repository')) {
273
+ if (errorMessage.toLowerCase().includes("not a git repository")) {
166
274
  throw new McpError(BaseErrorCode.NOT_FOUND, `Path is not a Git repository: ${targetPath}`, { context, operation, originalError: error });
167
275
  }
168
- if (input.mode === 'create' && errorMessage.includes('already exists')) {
169
- return { success: false, mode: 'create', message: `Failed to create branch: Branch '${input.branchName}' already exists. Use force=true to overwrite.`, error: errorMessage };
276
+ if (input.mode === "create" && errorMessage.includes("already exists")) {
277
+ return {
278
+ success: false,
279
+ mode: "create",
280
+ message: `Failed to create branch: Branch '${input.branchName}' already exists. Use force=true to overwrite.`,
281
+ error: errorMessage,
282
+ };
170
283
  }
171
- if (input.mode === 'delete' && errorMessage.includes('not found')) {
172
- return { success: false, mode: 'delete', message: `Failed to delete branch: Branch '${input.branchName}' not found.`, error: errorMessage };
284
+ if (input.mode === "delete" && errorMessage.includes("not found")) {
285
+ return {
286
+ success: false,
287
+ mode: "delete",
288
+ message: `Failed to delete branch: Branch '${input.branchName}' not found.`,
289
+ error: errorMessage,
290
+ };
173
291
  }
174
- if (input.mode === 'delete' && errorMessage.includes('not fully merged')) {
175
- return { success: false, mode: 'delete', message: `Failed to delete branch: Branch '${input.branchName}' is not fully merged. Use force=true to delete.`, error: errorMessage };
292
+ if (input.mode === "delete" && errorMessage.includes("not fully merged")) {
293
+ return {
294
+ success: false,
295
+ mode: "delete",
296
+ message: `Failed to delete branch: Branch '${input.branchName}' is not fully merged. Use force=true to delete.`,
297
+ error: errorMessage,
298
+ };
176
299
  }
177
- if (input.mode === 'rename' && errorMessage.includes('already exists')) {
178
- return { success: false, mode: 'rename', message: `Failed to rename branch: Branch '${input.newBranchName}' already exists. Use force=true to overwrite.`, error: errorMessage };
300
+ if (input.mode === "rename" && errorMessage.includes("already exists")) {
301
+ return {
302
+ success: false,
303
+ mode: "rename",
304
+ message: `Failed to rename branch: Branch '${input.newBranchName}' already exists. Use force=true to overwrite.`,
305
+ error: errorMessage,
306
+ };
179
307
  }
180
- if (input.mode === 'rename' && errorMessage.includes('not found')) {
181
- return { success: false, mode: 'rename', message: `Failed to rename branch: Branch '${input.branchName}' not found.`, error: errorMessage };
308
+ if (input.mode === "rename" && errorMessage.includes("not found")) {
309
+ return {
310
+ success: false,
311
+ mode: "rename",
312
+ message: `Failed to rename branch: Branch '${input.branchName}' not found.`,
313
+ error: errorMessage,
314
+ };
182
315
  }
183
- // Return structured failure for other git errors
184
- return {
185
- success: false,
186
- mode: input.mode,
187
- message: `Git branch ${input.mode} failed for path: ${targetPath}.`,
188
- error: errorMessage
189
- };
316
+ // Throw a generic McpError for other failures
317
+ throw new McpError(BaseErrorCode.INTERNAL_ERROR, `Git branch ${input.mode} failed for path: ${targetPath}. Error: ${errorMessage}`, { context, operation, originalError: error });
190
318
  }
191
319
  }
@@ -1,11 +1,11 @@
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 { BaseErrorCode } from '../../../types-global/errors.js'; // Keep direct import for types-global
7
- import { requestContextService } from '../../../utils/index.js';
8
- import { GitBranchBaseSchema, gitBranchLogic } from './logic.js';
6
+ import { BaseErrorCode } from "../../../types-global/errors.js"; // Keep direct import for types-global
7
+ import { requestContextService } from "../../../utils/index.js";
8
+ import { GitBranchBaseSchema, gitBranchLogic, } from "./logic.js";
9
9
  let _getWorkingDirectory;
10
10
  let _getSessionId;
11
11
  /**
@@ -16,10 +16,10 @@ let _getSessionId;
16
16
  export function initializeGitBranchStateAccessors(getWdFn, getSidFn) {
17
17
  _getWorkingDirectory = getWdFn;
18
18
  _getSessionId = getSidFn;
19
- logger.info('State accessors initialized for git_branch tool registration.');
19
+ logger.info("State accessors initialized for git_branch tool registration.");
20
20
  }
21
- const TOOL_NAME = 'git_branch';
22
- const TOOL_DESCRIPTION = 'Manages Git branches. Supports listing (local, remote, all), creating, deleting (with force), renaming (with force), and showing the current branch. Returns results as a JSON object.';
21
+ const TOOL_NAME = "git_branch";
22
+ const TOOL_DESCRIPTION = "Manages Git branches. Supports listing (local, remote, all), creating, deleting (with force), renaming (with force), and showing the current branch. Returns results as a JSON object.";
23
23
  /**
24
24
  * Registers the git_branch tool with the MCP server.
25
25
  *
@@ -29,9 +29,9 @@ const TOOL_DESCRIPTION = 'Manages Git branches. Supports listing (local, remote,
29
29
  */
30
30
  export const registerGitBranchTool = async (server) => {
31
31
  if (!_getWorkingDirectory || !_getSessionId) {
32
- throw new Error('State accessors for git_branch must be initialized before registration.');
32
+ throw new Error("State accessors for git_branch must be initialized before registration.");
33
33
  }
34
- const operation = 'registerGitBranchTool';
34
+ const operation = "registerGitBranchTool";
35
35
  const context = requestContextService.createRequestContext({ operation });
36
36
  await ErrorHandler.tryCatch(async () => {
37
37
  // Register using the BASE schema shape
@@ -40,7 +40,10 @@ export const registerGitBranchTool = async (server) => {
40
40
  async (validatedArgs, callContext) => {
41
41
  const toolInput = validatedArgs; // Cast for use
42
42
  const toolOperation = `tool:${TOOL_NAME}:${toolInput.mode}`;
43
- const requestContext = requestContextService.createRequestContext({ operation: toolOperation, parentContext: callContext });
43
+ const requestContext = requestContextService.createRequestContext({
44
+ operation: toolOperation,
45
+ parentContext: callContext,
46
+ });
44
47
  const sessionId = _getSessionId(requestContext);
45
48
  const getWorkingDirectoryForSession = () => _getWorkingDirectory(sessionId);
46
49
  const logicContext = {
@@ -52,9 +55,9 @@ export const registerGitBranchTool = async (server) => {
52
55
  return await ErrorHandler.tryCatch(async () => {
53
56
  const branchResult = await gitBranchLogic(toolInput, logicContext);
54
57
  const resultContent = {
55
- type: 'text',
58
+ type: "text",
56
59
  text: JSON.stringify(branchResult, null, 2), // Pretty-print JSON
57
- contentType: 'application/json',
60
+ contentType: "application/json",
58
61
  };
59
62
  if (branchResult.success) {
60
63
  logger.info(`Tool ${TOOL_NAME} (mode: ${toolInput.mode}) executed successfully, returning JSON`, logicContext);
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @fileoverview Barrel file for the gitCheckout tool.
3
3
  */
4
- export { registerGitCheckoutTool, initializeGitCheckoutStateAccessors } from './registration.js';
4
+ export { registerGitCheckoutTool, initializeGitCheckoutStateAccessors, } from "./registration.js";
5
5
  // Export types if needed elsewhere, e.g.:
6
6
  // export type { GitCheckoutInput, GitCheckoutResult } from './logic.js';