@cyanheads/git-mcp-server 2.2.0 → 2.2.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 (51) hide show
  1. package/dist/mcp-server/tools/gitAdd/logic.js +26 -45
  2. package/dist/mcp-server/tools/gitAdd/registration.js +6 -5
  3. package/dist/mcp-server/tools/gitBranch/logic.js +34 -51
  4. package/dist/mcp-server/tools/gitBranch/registration.js +7 -7
  5. package/dist/mcp-server/tools/gitCheckout/logic.js +15 -37
  6. package/dist/mcp-server/tools/gitCheckout/registration.js +7 -7
  7. package/dist/mcp-server/tools/gitCherryPick/logic.js +9 -28
  8. package/dist/mcp-server/tools/gitCherryPick/registration.js +7 -7
  9. package/dist/mcp-server/tools/gitClean/logic.js +9 -19
  10. package/dist/mcp-server/tools/gitClean/registration.js +7 -7
  11. package/dist/mcp-server/tools/gitClearWorkingDir/logic.js +4 -11
  12. package/dist/mcp-server/tools/gitClearWorkingDir/registration.js +7 -7
  13. package/dist/mcp-server/tools/gitClone/logic.js +10 -29
  14. package/dist/mcp-server/tools/gitClone/registration.js +7 -7
  15. package/dist/mcp-server/tools/gitCommit/logic.js +22 -52
  16. package/dist/mcp-server/tools/gitCommit/registration.js +7 -7
  17. package/dist/mcp-server/tools/gitDiff/logic.js +21 -37
  18. package/dist/mcp-server/tools/gitDiff/registration.js +7 -7
  19. package/dist/mcp-server/tools/gitFetch/logic.js +4 -20
  20. package/dist/mcp-server/tools/gitFetch/registration.js +7 -7
  21. package/dist/mcp-server/tools/gitInit/logic.js +10 -26
  22. package/dist/mcp-server/tools/gitInit/registration.js +7 -7
  23. package/dist/mcp-server/tools/gitLog/logic.js +19 -32
  24. package/dist/mcp-server/tools/gitLog/registration.js +7 -7
  25. package/dist/mcp-server/tools/gitMerge/logic.js +9 -28
  26. package/dist/mcp-server/tools/gitMerge/registration.js +7 -7
  27. package/dist/mcp-server/tools/gitPull/logic.js +4 -23
  28. package/dist/mcp-server/tools/gitPull/registration.js +7 -7
  29. package/dist/mcp-server/tools/gitPush/logic.js +9 -28
  30. package/dist/mcp-server/tools/gitPush/registration.js +7 -7
  31. package/dist/mcp-server/tools/gitRebase/logic.js +9 -28
  32. package/dist/mcp-server/tools/gitRebase/registration.js +7 -7
  33. package/dist/mcp-server/tools/gitRemote/logic.js +22 -38
  34. package/dist/mcp-server/tools/gitRemote/registration.js +7 -7
  35. package/dist/mcp-server/tools/gitReset/logic.js +5 -21
  36. package/dist/mcp-server/tools/gitReset/registration.js +7 -7
  37. package/dist/mcp-server/tools/gitSetWorkingDir/logic.js +8 -25
  38. package/dist/mcp-server/tools/gitSetWorkingDir/registration.js +7 -7
  39. package/dist/mcp-server/tools/gitShow/logic.js +3 -19
  40. package/dist/mcp-server/tools/gitShow/registration.js +7 -7
  41. package/dist/mcp-server/tools/gitStash/logic.js +14 -30
  42. package/dist/mcp-server/tools/gitStash/registration.js +7 -7
  43. package/dist/mcp-server/tools/gitStatus/logic.js +3 -13
  44. package/dist/mcp-server/tools/gitStatus/registration.js +7 -7
  45. package/dist/mcp-server/tools/gitTag/logic.js +6 -25
  46. package/dist/mcp-server/tools/gitTag/registration.js +7 -7
  47. package/dist/mcp-server/tools/gitWorktree/logic.js +5 -21
  48. package/dist/mcp-server/tools/gitWorktree/registration.js +7 -7
  49. package/dist/mcp-server/tools/gitWrapupInstructions/logic.js +5 -7
  50. package/dist/mcp-server/tools/gitWrapupInstructions/registration.js +8 -8
  51. package/package.json +5 -5
@@ -3,7 +3,6 @@
3
3
  * @module src/mcp-server/tools/gitPull/registration
4
4
  */
5
5
  import { ErrorHandler, logger, requestContextService } from "../../../utils/index.js";
6
- import { McpError, BaseErrorCode } from "../../../types-global/errors.js";
7
6
  import { pullGitChanges, GitPullInputSchema, GitPullOutputSchema, } from "./logic.js";
8
7
  const TOOL_NAME = "git_pull";
9
8
  const TOOL_DESCRIPTION = "Fetches from and integrates with another repository or a local branch (e.g., 'git pull origin main'). Supports rebase and fast-forward only options. Returns the pull result as a JSON object.";
@@ -45,18 +44,19 @@ export const registerGitPullTool = async (server, getWorkingDirectory, getSessio
45
44
  }
46
45
  catch (error) {
47
46
  logger.error(`Error in ${TOOL_NAME} handler`, { error, ...handlerContext });
48
- const handledError = ErrorHandler.handleError(error, {
47
+ const mcpError = ErrorHandler.handleError(error, {
49
48
  operation: `tool:${TOOL_NAME}`,
50
49
  context: handlerContext,
51
50
  input: params,
52
51
  });
53
- const mcpError = handledError instanceof McpError
54
- ? handledError
55
- : new McpError(BaseErrorCode.INTERNAL_ERROR, "An unexpected error occurred.", { originalError: handledError });
56
52
  return {
57
53
  isError: true,
58
- content: [{ type: "text", text: mcpError.message }],
59
- structuredContent: mcpError.details,
54
+ content: [{ type: "text", text: `Error: ${mcpError.message}` }],
55
+ structuredContent: {
56
+ code: mcpError.code,
57
+ message: mcpError.message,
58
+ details: mcpError.details,
59
+ },
60
60
  };
61
61
  }
62
62
  });
@@ -66,32 +66,13 @@ export async function pushGitChanges(params, context) {
66
66
  args.push(params.branch);
67
67
  }
68
68
  }
69
- try {
70
- logger.debug(`Executing command: git ${args.join(" ")}`, { ...context, operation });
71
- const { stdout, stderr } = await execFileAsync("git", args);
72
- const message = stderr.trim() || stdout.trim() || "Push command executed successfully.";
73
- return {
74
- success: true,
75
- message,
76
- rejected: message.includes("[rejected]"),
77
- deleted: message.includes("[deleted]"),
78
- };
79
- }
80
- catch (error) {
81
- const errorMessage = error.stderr || error.stdout || error.message || "";
82
- logger.error(`Failed to execute git push command`, { ...context, operation, errorMessage });
83
- if (errorMessage.toLowerCase().includes("not a git repository")) {
84
- throw new McpError(BaseErrorCode.NOT_FOUND, `Path is not a Git repository: ${targetPath}`);
85
- }
86
- if (errorMessage.includes("Could not read from remote repository")) {
87
- throw new McpError(BaseErrorCode.SERVICE_UNAVAILABLE, "Failed to connect to remote repository.");
88
- }
89
- if (errorMessage.includes("rejected")) {
90
- throw new McpError(BaseErrorCode.CONFLICT, `Push rejected: ${errorMessage}`);
91
- }
92
- if (errorMessage.includes("Authentication failed")) {
93
- throw new McpError(BaseErrorCode.UNAUTHORIZED, "Authentication failed for remote repository.");
94
- }
95
- throw new McpError(BaseErrorCode.INTERNAL_ERROR, `Git push failed: ${errorMessage}`);
96
- }
69
+ logger.debug(`Executing command: git ${args.join(" ")}`, { ...context, operation });
70
+ const { stdout, stderr } = await execFileAsync("git", args);
71
+ const message = stderr.trim() || stdout.trim() || "Push command executed successfully.";
72
+ return {
73
+ success: true,
74
+ message,
75
+ rejected: message.includes("[rejected]"),
76
+ deleted: message.includes("[deleted]"),
77
+ };
97
78
  }
@@ -3,7 +3,6 @@
3
3
  * @module src/mcp-server/tools/gitPush/registration
4
4
  */
5
5
  import { ErrorHandler, logger, requestContextService } from "../../../utils/index.js";
6
- import { McpError, BaseErrorCode } from "../../../types-global/errors.js";
7
6
  import { pushGitChanges, GitPushOutputSchema, GitPushBaseSchema, } from "./logic.js";
8
7
  const TOOL_NAME = "git_push";
9
8
  const TOOL_DESCRIPTION = "Updates remote refs using local refs, sending objects necessary to complete the given refs. Supports pushing specific branches, tags, forcing, setting upstream, and deleting remote branches. Returns the push result as a JSON object.";
@@ -45,18 +44,19 @@ export const registerGitPushTool = async (server, getWorkingDirectory, getSessio
45
44
  }
46
45
  catch (error) {
47
46
  logger.error(`Error in ${TOOL_NAME} handler`, { error, ...handlerContext });
48
- const handledError = ErrorHandler.handleError(error, {
47
+ const mcpError = ErrorHandler.handleError(error, {
49
48
  operation: `tool:${TOOL_NAME}`,
50
49
  context: handlerContext,
51
50
  input: params,
52
51
  });
53
- const mcpError = handledError instanceof McpError
54
- ? handledError
55
- : new McpError(BaseErrorCode.INTERNAL_ERROR, "An unexpected error occurred.", { originalError: handledError });
56
52
  return {
57
53
  isError: true,
58
- content: [{ type: "text", text: mcpError.message }],
59
- structuredContent: mcpError.details,
54
+ content: [{ type: "text", text: `Error: ${mcpError.message}` }],
55
+ structuredContent: {
56
+ code: mcpError.code,
57
+ message: mcpError.message,
58
+ details: mcpError.details,
59
+ },
60
60
  };
61
61
  }
62
62
  });
@@ -62,32 +62,13 @@ export async function gitRebaseLogic(params, context) {
62
62
  args.push(`--${params.mode}`);
63
63
  break;
64
64
  }
65
- try {
66
- logger.debug(`Executing command: git ${args.join(" ")}`, { ...context, operation });
67
- const { stdout, stderr } = await execFileAsync("git", args);
68
- const output = stdout + stderr;
69
- return {
70
- success: true,
71
- message: `Rebase ${params.mode} executed successfully.`,
72
- rebaseCompleted: /successfully rebased/i.test(output),
73
- needsManualAction: /conflict|stopped at|edit/i.test(output),
74
- };
75
- }
76
- catch (error) {
77
- const errorMessage = error.stderr || error.stdout || error.message || "";
78
- logger.error(`Git rebase ${params.mode} command failed`, { ...context, operation, errorMessage });
79
- if (/conflict/i.test(errorMessage)) {
80
- throw new McpError(BaseErrorCode.CONFLICT, `Rebase failed due to conflicts. Please resolve them and use 'git rebase --continue'.`);
81
- }
82
- if (/no rebase in progress/i.test(errorMessage)) {
83
- throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Failed to ${params.mode} rebase: No rebase is currently in progress.`);
84
- }
85
- if (/your local changes would be overwritten/i.test(errorMessage)) {
86
- throw new McpError(BaseErrorCode.CONFLICT, "Rebase failed: Your local changes would be overwritten. Please commit or stash them.");
87
- }
88
- if (/not a git repository/i.test(errorMessage)) {
89
- throw new McpError(BaseErrorCode.NOT_FOUND, `Path is not a Git repository: ${targetPath}`);
90
- }
91
- throw new McpError(BaseErrorCode.INTERNAL_ERROR, `Git rebase ${params.mode} failed: ${errorMessage}`);
92
- }
65
+ logger.debug(`Executing command: git ${args.join(" ")}`, { ...context, operation });
66
+ const { stdout, stderr } = await execFileAsync("git", args);
67
+ const output = stdout + stderr;
68
+ return {
69
+ success: true,
70
+ message: `Rebase ${params.mode} executed successfully.`,
71
+ rebaseCompleted: /successfully rebased/i.test(output),
72
+ needsManualAction: /conflict|stopped at|edit/i.test(output),
73
+ };
93
74
  }
@@ -3,7 +3,6 @@
3
3
  * @module src/mcp-server/tools/gitRebase/registration
4
4
  */
5
5
  import { ErrorHandler, logger, requestContextService } from "../../../utils/index.js";
6
- import { McpError, BaseErrorCode } from "../../../types-global/errors.js";
7
6
  import { gitRebaseLogic, GitRebaseOutputSchema, GitRebaseBaseSchema, } from "./logic.js";
8
7
  const TOOL_NAME = "git_rebase";
9
8
  const TOOL_DESCRIPTION = "Reapplies commits on top of another base tip. Supports starting a rebase (standard or interactive), continuing, aborting, or skipping steps in an ongoing rebase. Returns results as a JSON object.";
@@ -45,18 +44,19 @@ export const registerGitRebaseTool = async (server, getWorkingDirectory, getSess
45
44
  }
46
45
  catch (error) {
47
46
  logger.error(`Error in ${TOOL_NAME} handler`, { error, ...handlerContext });
48
- const handledError = ErrorHandler.handleError(error, {
47
+ const mcpError = ErrorHandler.handleError(error, {
49
48
  operation: `tool:${TOOL_NAME}`,
50
49
  context: handlerContext,
51
50
  input: params,
52
51
  });
53
- const mcpError = handledError instanceof McpError
54
- ? handledError
55
- : new McpError(BaseErrorCode.INTERNAL_ERROR, "An unexpected error occurred.", { originalError: handledError });
56
52
  return {
57
53
  isError: true,
58
- content: [{ type: "text", text: mcpError.message }],
59
- structuredContent: mcpError.details,
54
+ content: [{ type: "text", text: `Error: ${mcpError.message}` }],
55
+ structuredContent: {
56
+ code: mcpError.code,
57
+ message: mcpError.message,
58
+ details: mcpError.details,
59
+ },
60
60
  };
61
61
  }
62
62
  });
@@ -62,44 +62,28 @@ export async function gitRemoteLogic(params, context) {
62
62
  args.push("show", params.name);
63
63
  break;
64
64
  }
65
- try {
66
- logger.debug(`Executing command: git ${args.join(" ")}`, { ...context, operation });
67
- const { stdout } = await execFileAsync("git", args);
68
- if (params.mode === 'list') {
69
- const remoteMap = new Map();
70
- stdout.trim().split("\n").forEach(line => {
71
- const parts = line.split(/\s+/);
72
- if (parts.length < 3)
73
- return;
74
- const [name, url, type] = parts;
75
- const cleanType = type.replace(/[()]/g, "");
76
- if (!remoteMap.has(name))
77
- remoteMap.set(name, {});
78
- if (cleanType === 'fetch')
79
- remoteMap.get(name).fetchUrl = url;
80
- if (cleanType === 'push')
81
- remoteMap.get(name).pushUrl = url;
82
- });
83
- const remotes = Array.from(remoteMap.entries()).map(([name, urls]) => ({ name, fetchUrl: urls.fetchUrl || 'N/A', pushUrl: urls.pushUrl || urls.fetchUrl || 'N/A' }));
84
- return { success: true, mode: params.mode, remotes };
85
- }
86
- if (params.mode === 'show') {
87
- return { success: true, mode: params.mode, details: stdout.trim() };
88
- }
89
- return { success: true, mode: params.mode, message: `Remote '${params.name}' ${params.mode === 'add' ? 'added' : 'removed'} successfully.` };
65
+ logger.debug(`Executing command: git ${args.join(" ")}`, { ...context, operation });
66
+ const { stdout } = await execFileAsync("git", args);
67
+ if (params.mode === 'list') {
68
+ const remoteMap = new Map();
69
+ stdout.trim().split("\n").forEach(line => {
70
+ const parts = line.split(/\s+/);
71
+ if (parts.length < 3)
72
+ return;
73
+ const [name, url, type] = parts;
74
+ const cleanType = type.replace(/[()]/g, "");
75
+ if (!remoteMap.has(name))
76
+ remoteMap.set(name, {});
77
+ if (cleanType === 'fetch')
78
+ remoteMap.get(name).fetchUrl = url;
79
+ if (cleanType === 'push')
80
+ remoteMap.get(name).pushUrl = url;
81
+ });
82
+ const remotes = Array.from(remoteMap.entries()).map(([name, urls]) => ({ name, fetchUrl: urls.fetchUrl || 'N/A', pushUrl: urls.pushUrl || urls.fetchUrl || 'N/A' }));
83
+ return { success: true, mode: params.mode, remotes };
90
84
  }
91
- catch (error) {
92
- const errorMessage = error.stderr || error.message || "";
93
- logger.error(`Failed to execute git remote command`, { ...context, operation, errorMessage });
94
- if (errorMessage.toLowerCase().includes("not a git repository")) {
95
- throw new McpError(BaseErrorCode.NOT_FOUND, `Path is not a Git repository: ${targetPath}`);
96
- }
97
- if (params.mode === "add" && errorMessage.toLowerCase().includes("already exists")) {
98
- throw new McpError(BaseErrorCode.CONFLICT, `Remote '${params.name}' already exists.`);
99
- }
100
- if ((params.mode === "remove" || params.mode === "show") && errorMessage.toLowerCase().includes("no such remote")) {
101
- throw new McpError(BaseErrorCode.NOT_FOUND, `Remote '${params.name}' does not exist.`);
102
- }
103
- throw new McpError(BaseErrorCode.INTERNAL_ERROR, `Git remote ${params.mode} failed: ${errorMessage}`);
85
+ if (params.mode === 'show') {
86
+ return { success: true, mode: params.mode, details: stdout.trim() };
104
87
  }
88
+ return { success: true, mode: params.mode, message: `Remote '${params.name}' ${params.mode === 'add' ? 'added' : 'removed'} successfully.` };
105
89
  }
@@ -3,7 +3,6 @@
3
3
  * @module src/mcp-server/tools/gitRemote/registration
4
4
  */
5
5
  import { ErrorHandler, logger, requestContextService } from "../../../utils/index.js";
6
- import { McpError, BaseErrorCode } from "../../../types-global/errors.js";
7
6
  import { gitRemoteLogic, GitRemoteOutputSchema, GitRemoteBaseSchema, } from "./logic.js";
8
7
  const TOOL_NAME = "git_remote";
9
8
  const TOOL_DESCRIPTION = "Manages remote repositories (list, add, remove, show).";
@@ -45,18 +44,19 @@ export const registerGitRemoteTool = async (server, getWorkingDirectory, getSess
45
44
  }
46
45
  catch (error) {
47
46
  logger.error(`Error in ${TOOL_NAME} handler`, { error, ...handlerContext });
48
- const handledError = ErrorHandler.handleError(error, {
47
+ const mcpError = ErrorHandler.handleError(error, {
49
48
  operation: `tool:${TOOL_NAME}`,
50
49
  context: handlerContext,
51
50
  input: params,
52
51
  });
53
- const mcpError = handledError instanceof McpError
54
- ? handledError
55
- : new McpError(BaseErrorCode.INTERNAL_ERROR, "An unexpected error occurred.", { originalError: handledError });
56
52
  return {
57
53
  isError: true,
58
- content: [{ type: "text", text: mcpError.message }],
59
- structuredContent: mcpError.details,
54
+ content: [{ type: "text", text: `Error: ${mcpError.message}` }],
55
+ structuredContent: {
56
+ code: mcpError.code,
57
+ message: mcpError.message,
58
+ details: mcpError.details,
59
+ },
60
60
  };
61
61
  }
62
62
  });
@@ -36,25 +36,9 @@ export async function resetGitState(params, context) {
36
36
  args.push(`--${params.mode}`);
37
37
  if (params.commit)
38
38
  args.push(params.commit);
39
- try {
40
- logger.debug(`Executing command: git ${args.join(" ")}`, { ...context, operation });
41
- const { stdout, stderr } = await execFileAsync("git", args);
42
- const message = stderr.trim() || stdout.trim() || `Reset successful (mode: ${params.mode}).`;
43
- const changesSummary = stderr.includes("Unstaged changes after reset") ? stderr : undefined;
44
- return { success: true, message, changesSummary };
45
- }
46
- catch (error) {
47
- const errorMessage = error.stderr || error.stdout || error.message || "";
48
- logger.error(`Failed to execute git reset command`, { ...context, operation, errorMessage });
49
- if (errorMessage.toLowerCase().includes("not a git repository")) {
50
- throw new McpError(BaseErrorCode.NOT_FOUND, `Path is not a Git repository: ${targetPath}`);
51
- }
52
- if (errorMessage.includes("bad revision")) {
53
- throw new McpError(BaseErrorCode.NOT_FOUND, `Invalid commit reference specified: '${params.commit}'.`);
54
- }
55
- if (errorMessage.includes("unmerged paths")) {
56
- throw new McpError(BaseErrorCode.CONFLICT, "Cannot reset due to unmerged files. Please resolve conflicts first.");
57
- }
58
- throw new McpError(BaseErrorCode.INTERNAL_ERROR, `Git reset failed: ${errorMessage}`);
59
- }
39
+ logger.debug(`Executing command: git ${args.join(" ")}`, { ...context, operation });
40
+ const { stdout, stderr } = await execFileAsync("git", args);
41
+ const message = stderr.trim() || stdout.trim() || `Reset successful (mode: ${params.mode}).`;
42
+ const changesSummary = stderr.includes("Unstaged changes after reset") ? stderr : undefined;
43
+ return { success: true, message, changesSummary };
60
44
  }
@@ -3,7 +3,6 @@
3
3
  * @module src/mcp-server/tools/gitReset/registration
4
4
  */
5
5
  import { ErrorHandler, logger, requestContextService } from "../../../utils/index.js";
6
- import { McpError, BaseErrorCode } from "../../../types-global/errors.js";
7
6
  import { resetGitState, GitResetInputSchema, GitResetOutputSchema, } from "./logic.js";
8
7
  const TOOL_NAME = "git_reset";
9
8
  const TOOL_DESCRIPTION = "Resets the current HEAD to a specified state. Supports different modes ('soft', 'mixed', 'hard', 'merge', 'keep') to control how the index and working tree are affected. Can reset to a specific commit. USE 'hard' MODE WITH EXTREME CAUTION as it discards local changes.";
@@ -45,18 +44,19 @@ export const registerGitResetTool = async (server, getWorkingDirectory, getSessi
45
44
  }
46
45
  catch (error) {
47
46
  logger.error(`Error in ${TOOL_NAME} handler`, { error, ...handlerContext });
48
- const handledError = ErrorHandler.handleError(error, {
47
+ const mcpError = ErrorHandler.handleError(error, {
49
48
  operation: `tool:${TOOL_NAME}`,
50
49
  context: handlerContext,
51
50
  input: params,
52
51
  });
53
- const mcpError = handledError instanceof McpError
54
- ? handledError
55
- : new McpError(BaseErrorCode.INTERNAL_ERROR, "An unexpected error occurred.", { originalError: handledError });
56
52
  return {
57
53
  isError: true,
58
- content: [{ type: "text", text: mcpError.message }],
59
- structuredContent: mcpError.details,
54
+ content: [{ type: "text", text: `Error: ${mcpError.message}` }],
55
+ structuredContent: {
56
+ code: mcpError.code,
57
+ message: mcpError.message,
58
+ details: mcpError.details,
59
+ },
60
60
  };
61
61
  }
62
62
  });
@@ -23,13 +23,8 @@ export const GitSetWorkingDirOutputSchema = z.object({
23
23
  initialized: z.boolean().describe("Indicates if a new repository was initialized."),
24
24
  });
25
25
  async function checkIsGitRepo(path) {
26
- try {
27
- const { stdout } = await execFileAsync("git", ["rev-parse", "--is-inside-work-tree"], { cwd: path });
28
- return stdout.trim() === "true";
29
- }
30
- catch {
31
- return false;
32
- }
26
+ const { stdout } = await execFileAsync("git", ["rev-parse", "--is-inside-work-tree"], { cwd: path }).catch(() => ({ stdout: "false" }));
27
+ return stdout.trim() === "true";
33
28
  }
34
29
  /**
35
30
  * 4. IMPLEMENT the core logic function.
@@ -39,28 +34,16 @@ export async function gitSetWorkingDirLogic(params, context) {
39
34
  const operation = "gitSetWorkingDirLogic";
40
35
  logger.debug(`Executing ${operation}`, { ...context, params });
41
36
  const sanitizedPath = sanitization.sanitizePath(params.path, { allowAbsolute: true }).sanitizedPath;
42
- try {
43
- const stats = await fs.stat(sanitizedPath);
44
- if (!stats.isDirectory()) {
45
- throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Path is not a directory: ${sanitizedPath}`);
46
- }
47
- }
48
- catch (error) {
49
- if (error instanceof McpError)
50
- throw error;
51
- throw new McpError(BaseErrorCode.NOT_FOUND, `Directory does not exist or is inaccessible: ${sanitizedPath}`);
37
+ const stats = await fs.stat(sanitizedPath);
38
+ if (!stats.isDirectory()) {
39
+ throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Path is not a directory: ${sanitizedPath}`);
52
40
  }
53
41
  let isGitRepo = await checkIsGitRepo(sanitizedPath);
54
42
  let initializedRepo = false;
55
43
  if (!isGitRepo && params.initializeIfNotPresent) {
56
- try {
57
- await execFileAsync("git", ["init", "--initial-branch=main"], { cwd: sanitizedPath });
58
- initializedRepo = true;
59
- isGitRepo = true;
60
- }
61
- catch (initError) {
62
- throw new McpError(BaseErrorCode.INTERNAL_ERROR, `Failed to initialize Git repository: ${initError.message}`);
63
- }
44
+ await execFileAsync("git", ["init", "--initial-branch=main"], { cwd: sanitizedPath });
45
+ initializedRepo = true;
46
+ isGitRepo = true;
64
47
  }
65
48
  if (params.validateGitRepo && !isGitRepo) {
66
49
  throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Path is not a valid Git repository: ${sanitizedPath}.`);
@@ -3,7 +3,6 @@
3
3
  * @module src/mcp-server/tools/gitSetWorkingDir/registration
4
4
  */
5
5
  import { ErrorHandler, logger, requestContextService } from "../../../utils/index.js";
6
- import { McpError, BaseErrorCode } from "../../../types-global/errors.js";
7
6
  import { gitSetWorkingDirLogic, GitSetWorkingDirInputSchema, GitSetWorkingDirOutputSchema, } from "./logic.js";
8
7
  const TOOL_NAME = "git_set_working_dir";
9
8
  const TOOL_DESCRIPTION = "Sets the default working directory for the current session. Subsequent Git tool calls within this session can use '.' for the `path` parameter, which will resolve to this directory. Optionally validates if the path is a Git repository (`validateGitRepo: true`). Can optionally initialize a Git repository with 'git init' if it's not already one and `initializeIfNotPresent: true` is set. Returns the result as a JSON object. IMPORTANT: The provided path must be absolute.";
@@ -45,18 +44,19 @@ export const registerGitSetWorkingDirTool = async (server, setWorkingDirectory,
45
44
  }
46
45
  catch (error) {
47
46
  logger.error(`Error in ${TOOL_NAME} handler`, { error, ...handlerContext });
48
- const handledError = ErrorHandler.handleError(error, {
47
+ const mcpError = ErrorHandler.handleError(error, {
49
48
  operation: `tool:${TOOL_NAME}`,
50
49
  context: handlerContext,
51
50
  input: params,
52
51
  });
53
- const mcpError = handledError instanceof McpError
54
- ? handledError
55
- : new McpError(BaseErrorCode.INTERNAL_ERROR, "An unexpected error occurred.", { originalError: handledError });
56
52
  return {
57
53
  isError: true,
58
- content: [{ type: "text", text: mcpError.message }],
59
- structuredContent: mcpError.details,
54
+ content: [{ type: "text", text: `Error: ${mcpError.message}` }],
55
+ structuredContent: {
56
+ code: mcpError.code,
57
+ message: mcpError.message,
58
+ details: mcpError.details,
59
+ },
60
60
  };
61
61
  }
62
62
  });
@@ -33,23 +33,7 @@ export async function gitShowLogic(params, context) {
33
33
  const targetPath = sanitization.sanitizePath(params.path === "." ? workingDir : params.path, { allowAbsolute: true }).sanitizedPath;
34
34
  const refSpec = params.filePath ? `${params.ref}:${params.filePath}` : params.ref;
35
35
  const args = ["-C", targetPath, "show", refSpec];
36
- try {
37
- logger.debug(`Executing command: git ${args.join(" ")}`, { ...context, operation });
38
- const { stdout } = await execFileAsync("git", args);
39
- return { success: true, content: stdout };
40
- }
41
- catch (error) {
42
- const errorMessage = error.stderr || error.message || "";
43
- logger.error(`Failed to execute git show command`, { ...context, operation, errorMessage });
44
- if (errorMessage.toLowerCase().includes("not a git repository")) {
45
- throw new McpError(BaseErrorCode.NOT_FOUND, `Path is not a Git repository: ${targetPath}`);
46
- }
47
- if (/unknown revision or path not in the working tree/i.test(errorMessage)) {
48
- throw new McpError(BaseErrorCode.NOT_FOUND, `Reference or pathspec not found: '${refSpec}'.`);
49
- }
50
- if (/ambiguous argument/i.test(errorMessage)) {
51
- throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Reference '${params.ref}' is ambiguous.`);
52
- }
53
- throw new McpError(BaseErrorCode.INTERNAL_ERROR, `Git show failed: ${errorMessage}`);
54
- }
36
+ logger.debug(`Executing command: git ${args.join(" ")}`, { ...context, operation });
37
+ const { stdout } = await execFileAsync("git", args);
38
+ return { success: true, content: stdout };
55
39
  }
@@ -3,7 +3,6 @@
3
3
  * @module src/mcp-server/tools/gitShow/registration
4
4
  */
5
5
  import { ErrorHandler, logger, requestContextService } from "../../../utils/index.js";
6
- import { McpError, BaseErrorCode } from "../../../types-global/errors.js";
7
6
  import { gitShowLogic, GitShowInputSchema, GitShowOutputSchema, } from "./logic.js";
8
7
  const TOOL_NAME = "git_show";
9
8
  const TOOL_DESCRIPTION = "Shows information about Git objects (commits, tags, blobs, trees) based on a reference. Can optionally show the content of a specific file at that reference. Returns the raw output.";
@@ -45,18 +44,19 @@ export const registerGitShowTool = async (server, getWorkingDirectory, getSessio
45
44
  }
46
45
  catch (error) {
47
46
  logger.error(`Error in ${TOOL_NAME} handler`, { error, ...handlerContext });
48
- const handledError = ErrorHandler.handleError(error, {
47
+ const mcpError = ErrorHandler.handleError(error, {
49
48
  operation: `tool:${TOOL_NAME}`,
50
49
  context: handlerContext,
51
50
  input: params,
52
51
  });
53
- const mcpError = handledError instanceof McpError
54
- ? handledError
55
- : new McpError(BaseErrorCode.INTERNAL_ERROR, "An unexpected error occurred.", { originalError: handledError });
56
52
  return {
57
53
  isError: true,
58
- content: [{ type: "text", text: mcpError.message }],
59
- structuredContent: mcpError.details,
54
+ content: [{ type: "text", text: `Error: ${mcpError.message}` }],
55
+ structuredContent: {
56
+ code: mcpError.code,
57
+ message: mcpError.message,
58
+ details: mcpError.details,
59
+ },
60
60
  };
61
61
  }
62
62
  });
@@ -65,36 +65,20 @@ export async function gitStashLogic(params, context) {
65
65
  return baseArgs;
66
66
  };
67
67
  const args = buildArgs();
68
- try {
69
- logger.debug(`Executing command: git ${args.join(" ")}`, { ...context, operation });
70
- const { stdout, stderr } = await execFileAsync("git", args);
71
- if (params.mode === 'list') {
72
- const stashes = stdout.trim().split("\n").filter(Boolean).map(line => {
73
- const match = line.match(/^(stash@\{(\d+)\}):\s*(?:(?:WIP on|On)\s*([^:]+):\s*)?(.*)$/);
74
- return match ? { ref: match[1], branch: match[3] || "unknown", description: match[4] } : { ref: "unknown", branch: "unknown", description: line };
75
- });
76
- return { success: true, mode: params.mode, stashes };
77
- }
78
- const output = stdout + stderr;
79
- const conflicts = /conflict/i.test(output);
80
- if (params.mode === 'save') {
81
- const stashCreated = !/no local changes to save/i.test(output);
82
- return { success: true, mode: params.mode, message: stashCreated ? "Changes stashed." : "No local changes to save.", stashCreated };
83
- }
84
- return { success: true, mode: params.mode, message: `${params.mode} operation successful.`, conflicts };
68
+ logger.debug(`Executing command: git ${args.join(" ")}`, { ...context, operation });
69
+ const { stdout, stderr } = await execFileAsync("git", args);
70
+ if (params.mode === 'list') {
71
+ const stashes = stdout.trim().split("\n").filter(Boolean).map(line => {
72
+ const match = line.match(/^(stash@\{(\d+)\}):\s*(?:(?:WIP on|On)\s*([^:]+):\s*)?(.*)$/);
73
+ return match ? { ref: match[1], branch: match[3] || "unknown", description: match[4] } : { ref: "unknown", branch: "unknown", description: line };
74
+ });
75
+ return { success: true, mode: params.mode, stashes };
85
76
  }
86
- catch (error) {
87
- const errorMessage = error.stderr || error.message || "";
88
- logger.error(`Failed to execute git stash command`, { ...context, operation, errorMessage });
89
- if (errorMessage.toLowerCase().includes("not a git repository")) {
90
- throw new McpError(BaseErrorCode.NOT_FOUND, `Path is not a Git repository: ${targetPath}`);
91
- }
92
- if (/no such stash/i.test(errorMessage)) {
93
- throw new McpError(BaseErrorCode.NOT_FOUND, `Stash '${params.stashRef}' not found.`);
94
- }
95
- if (/conflict/i.test(errorMessage)) {
96
- throw new McpError(BaseErrorCode.CONFLICT, `Stash ${params.mode} failed due to conflicts.`);
97
- }
98
- throw new McpError(BaseErrorCode.INTERNAL_ERROR, `Git stash ${params.mode} failed: ${errorMessage}`);
77
+ const output = stdout + stderr;
78
+ const conflicts = /conflict/i.test(output);
79
+ if (params.mode === 'save') {
80
+ const stashCreated = !/no local changes to save/i.test(output);
81
+ return { success: true, mode: params.mode, message: stashCreated ? "Changes stashed." : "No local changes to save.", stashCreated };
99
82
  }
83
+ return { success: true, mode: params.mode, message: `${params.mode} operation successful.`, conflicts };
100
84
  }
@@ -3,7 +3,6 @@
3
3
  * @module src/mcp-server/tools/gitStash/registration
4
4
  */
5
5
  import { ErrorHandler, logger, requestContextService } from "../../../utils/index.js";
6
- import { McpError, BaseErrorCode } from "../../../types-global/errors.js";
7
6
  import { gitStashLogic, GitStashOutputSchema, GitStashBaseSchema, } from "./logic.js";
8
7
  const TOOL_NAME = "git_stash";
9
8
  const TOOL_DESCRIPTION = "Manages stashed changes in the working directory. Supports listing stashes, applying/popping specific stashes (with conflict detection), dropping stashes, and saving current changes to a new stash with an optional message. Returns results as a JSON object.";
@@ -45,18 +44,19 @@ export const registerGitStashTool = async (server, getWorkingDirectory, getSessi
45
44
  }
46
45
  catch (error) {
47
46
  logger.error(`Error in ${TOOL_NAME} handler`, { error, ...handlerContext });
48
- const handledError = ErrorHandler.handleError(error, {
47
+ const mcpError = ErrorHandler.handleError(error, {
49
48
  operation: `tool:${TOOL_NAME}`,
50
49
  context: handlerContext,
51
50
  input: params,
52
51
  });
53
- const mcpError = handledError instanceof McpError
54
- ? handledError
55
- : new McpError(BaseErrorCode.INTERNAL_ERROR, "An unexpected error occurred.", { originalError: handledError });
56
52
  return {
57
53
  isError: true,
58
- content: [{ type: "text", text: mcpError.message }],
59
- structuredContent: mcpError.details,
54
+ content: [{ type: "text", text: `Error: ${mcpError.message}` }],
55
+ structuredContent: {
56
+ code: mcpError.code,
57
+ message: mcpError.message,
58
+ details: mcpError.details,
59
+ },
60
60
  };
61
61
  }
62
62
  });
@@ -104,17 +104,7 @@ export async function getGitStatus(params, context) {
104
104
  }
105
105
  const targetPath = sanitization.sanitizePath(params.path === "." ? workingDir : params.path, { allowAbsolute: true }).sanitizedPath;
106
106
  const args = ["-C", targetPath, "status", "--porcelain=v1", "-b"];
107
- try {
108
- logger.debug(`Executing command: git ${args.join(" ")}`, { ...context, operation });
109
- const { stdout } = await execFileAsync("git", args);
110
- return parseGitStatus(stdout);
111
- }
112
- catch (error) {
113
- const errorMessage = error.stderr || error.message || "";
114
- logger.error(`Failed to execute git status command`, { ...context, operation, errorMessage });
115
- if (errorMessage.toLowerCase().includes("not a git repository")) {
116
- throw new McpError(BaseErrorCode.NOT_FOUND, `Path is not a Git repository: ${targetPath}`);
117
- }
118
- throw new McpError(BaseErrorCode.INTERNAL_ERROR, `Git status failed: ${errorMessage}`);
119
- }
107
+ logger.debug(`Executing command: git ${args.join(" ")}`, { ...context, operation });
108
+ const { stdout } = await execFileAsync("git", args);
109
+ return parseGitStatus(stdout);
120
110
  }