@cyanheads/git-mcp-server 2.1.8 → 2.2.0

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 (79) hide show
  1. package/README.md +4 -4
  2. package/dist/mcp-server/server.js +69 -228
  3. package/dist/mcp-server/tools/gitAdd/index.js +2 -4
  4. package/dist/mcp-server/tools/gitAdd/logic.js +17 -74
  5. package/dist/mcp-server/tools/gitAdd/registration.js +38 -59
  6. package/dist/mcp-server/tools/gitBranch/index.js +3 -5
  7. package/dist/mcp-server/tools/gitBranch/logic.js +118 -296
  8. package/dist/mcp-server/tools/gitBranch/registration.js +52 -66
  9. package/dist/mcp-server/tools/gitCheckout/index.js +2 -3
  10. package/dist/mcp-server/tools/gitCheckout/logic.js +47 -122
  11. package/dist/mcp-server/tools/gitCheckout/registration.js +53 -72
  12. package/dist/mcp-server/tools/gitCherryPick/index.js +3 -5
  13. package/dist/mcp-server/tools/gitCherryPick/logic.js +55 -162
  14. package/dist/mcp-server/tools/gitCherryPick/registration.js +52 -67
  15. package/dist/mcp-server/tools/gitClean/index.js +3 -5
  16. package/dist/mcp-server/tools/gitClean/logic.js +44 -143
  17. package/dist/mcp-server/tools/gitClean/registration.js +52 -92
  18. package/dist/mcp-server/tools/gitClearWorkingDir/index.js +3 -5
  19. package/dist/mcp-server/tools/gitClearWorkingDir/logic.js +19 -26
  20. package/dist/mcp-server/tools/gitClearWorkingDir/registration.js +55 -73
  21. package/dist/mcp-server/tools/gitClone/index.js +2 -4
  22. package/dist/mcp-server/tools/gitClone/logic.js +50 -171
  23. package/dist/mcp-server/tools/gitClone/registration.js +51 -42
  24. package/dist/mcp-server/tools/gitCommit/index.js +2 -4
  25. package/dist/mcp-server/tools/gitCommit/logic.js +90 -295
  26. package/dist/mcp-server/tools/gitCommit/registration.js +52 -73
  27. package/dist/mcp-server/tools/gitDiff/index.js +2 -3
  28. package/dist/mcp-server/tools/gitDiff/logic.js +78 -254
  29. package/dist/mcp-server/tools/gitDiff/registration.js +53 -68
  30. package/dist/mcp-server/tools/gitFetch/index.js +2 -3
  31. package/dist/mcp-server/tools/gitFetch/logic.js +47 -129
  32. package/dist/mcp-server/tools/gitFetch/registration.js +54 -66
  33. package/dist/mcp-server/tools/gitInit/index.js +3 -5
  34. package/dist/mcp-server/tools/gitInit/logic.js +46 -152
  35. package/dist/mcp-server/tools/gitInit/registration.js +52 -104
  36. package/dist/mcp-server/tools/gitLog/index.js +2 -3
  37. package/dist/mcp-server/tools/gitLog/logic.js +75 -257
  38. package/dist/mcp-server/tools/gitLog/registration.js +54 -66
  39. package/dist/mcp-server/tools/gitMerge/index.js +3 -5
  40. package/dist/mcp-server/tools/gitMerge/logic.js +52 -179
  41. package/dist/mcp-server/tools/gitMerge/registration.js +52 -71
  42. package/dist/mcp-server/tools/gitPull/index.js +2 -3
  43. package/dist/mcp-server/tools/gitPull/logic.js +48 -146
  44. package/dist/mcp-server/tools/gitPull/registration.js +53 -75
  45. package/dist/mcp-server/tools/gitPush/index.js +2 -3
  46. package/dist/mcp-server/tools/gitPush/logic.js +73 -181
  47. package/dist/mcp-server/tools/gitPush/registration.js +53 -75
  48. package/dist/mcp-server/tools/gitRebase/index.js +3 -5
  49. package/dist/mcp-server/tools/gitRebase/logic.js +73 -202
  50. package/dist/mcp-server/tools/gitRebase/registration.js +52 -70
  51. package/dist/mcp-server/tools/gitRemote/index.js +3 -5
  52. package/dist/mcp-server/tools/gitRemote/logic.js +85 -193
  53. package/dist/mcp-server/tools/gitRemote/registration.js +52 -65
  54. package/dist/mcp-server/tools/gitReset/index.js +2 -3
  55. package/dist/mcp-server/tools/gitReset/logic.js +37 -121
  56. package/dist/mcp-server/tools/gitReset/registration.js +53 -60
  57. package/dist/mcp-server/tools/gitSetWorkingDir/index.js +3 -5
  58. package/dist/mcp-server/tools/gitSetWorkingDir/logic.js +45 -133
  59. package/dist/mcp-server/tools/gitSetWorkingDir/registration.js +55 -85
  60. package/dist/mcp-server/tools/gitShow/index.js +3 -5
  61. package/dist/mcp-server/tools/gitShow/logic.js +33 -122
  62. package/dist/mcp-server/tools/gitShow/registration.js +52 -74
  63. package/dist/mcp-server/tools/gitStash/index.js +3 -5
  64. package/dist/mcp-server/tools/gitStash/logic.js +70 -214
  65. package/dist/mcp-server/tools/gitStash/registration.js +52 -77
  66. package/dist/mcp-server/tools/gitStatus/index.js +2 -4
  67. package/dist/mcp-server/tools/gitStatus/logic.js +82 -229
  68. package/dist/mcp-server/tools/gitStatus/registration.js +52 -66
  69. package/dist/mcp-server/tools/gitTag/index.js +3 -5
  70. package/dist/mcp-server/tools/gitTag/logic.js +66 -188
  71. package/dist/mcp-server/tools/gitTag/registration.js +54 -73
  72. package/dist/mcp-server/tools/gitWorktree/index.js +3 -5
  73. package/dist/mcp-server/tools/gitWorktree/logic.js +112 -322
  74. package/dist/mcp-server/tools/gitWorktree/registration.js +54 -58
  75. package/dist/mcp-server/tools/gitWrapupInstructions/index.js +5 -3
  76. package/dist/mcp-server/tools/gitWrapupInstructions/logic.js +26 -38
  77. package/dist/mcp-server/tools/gitWrapupInstructions/registration.js +54 -70
  78. package/dist/mcp-server/transports/httpTransport.js +2 -3
  79. package/package.json +8 -8
@@ -1,79 +1,64 @@
1
- // Import utils from barrel (ErrorHandler from ../utils/internal/errorHandler.js)
2
- import { ErrorHandler } from "../../../utils/index.js";
3
- // Import utils from barrel (logger from ../utils/internal/logger.js)
4
- import { logger } from "../../../utils/index.js";
5
- // Import utils from barrel (requestContextService, RequestContext from ../utils/internal/requestContext.js)
6
- import { requestContextService } from "../../../utils/index.js";
7
- // Import the shape and the final schema/types
8
- import { BaseErrorCode } from "../../../types-global/errors.js"; // Keep direct import for types-global
9
- import { diffGitChanges, GitDiffInputShape, } from "./logic.js";
10
- let _getWorkingDirectory;
11
- let _getSessionId;
12
1
  /**
13
- * Initializes the state accessors needed by the tool registration.
14
- * This should be called once during server setup.
15
- * @param getWdFn - Function to get the working directory for a session.
16
- * @param getSidFn - Function to get the session ID from context.
2
+ * @fileoverview Handles registration and error handling for the git_diff tool.
3
+ * @module src/mcp-server/tools/gitDiff/registration
17
4
  */
18
- export function initializeGitDiffStateAccessors(getWdFn, getSidFn) {
19
- _getWorkingDirectory = getWdFn;
20
- _getSessionId = getSidFn;
21
- logger.info("State accessors initialized for git_diff tool registration.");
22
- }
5
+ import { ErrorHandler, logger, requestContextService } from "../../../utils/index.js";
6
+ import { McpError, BaseErrorCode } from "../../../types-global/errors.js";
7
+ import { diffGitChanges, GitDiffOutputSchema, GitDiffBaseSchema, } from "./logic.js";
23
8
  const TOOL_NAME = "git_diff";
24
9
  const TOOL_DESCRIPTION = "Shows changes between commits, commit and working tree, etc. Can show staged changes or diff specific files. An optional 'includeUntracked' parameter (boolean) can be used to also show the content of untracked files. Returns the diff output as plain text.";
25
10
  /**
26
- * Registers the git_diff tool with the MCP server.
27
- *
28
- * @param {McpServer} server - The MCP server instance.
29
- * @throws {Error} If state accessors are not initialized.
11
+ * Registers the git_diff tool with the MCP server instance.
12
+ * @param server The MCP server instance.
13
+ * @param getWorkingDirectory Function to get the session's working directory.
14
+ * @param getSessionId Function to get the session ID from context.
30
15
  */
31
- export async function registerGitDiffTool(server) {
32
- if (!_getWorkingDirectory || !_getSessionId) {
33
- throw new Error("State accessors for git_diff must be initialized before registration.");
34
- }
16
+ export const registerGitDiffTool = async (server, getWorkingDirectory, getSessionId) => {
35
17
  const operation = "registerGitDiffTool";
36
18
  const context = requestContextService.createRequestContext({ operation });
37
- await ErrorHandler.tryCatch(async () => {
38
- // Use the exported shape for registration
39
- server.tool(TOOL_NAME, TOOL_DESCRIPTION, GitDiffInputShape, // Provide the Zod base schema shape
40
- async (validatedArgs, callContext) => {
41
- const toolOperation = "tool:git_diff";
42
- const requestContext = requestContextService.createRequestContext({
43
- operation: toolOperation,
44
- parentContext: callContext,
19
+ server.registerTool(TOOL_NAME, {
20
+ title: "Git Diff",
21
+ description: TOOL_DESCRIPTION,
22
+ inputSchema: GitDiffBaseSchema.shape,
23
+ outputSchema: GitDiffOutputSchema.shape,
24
+ annotations: {
25
+ readOnlyHint: true,
26
+ destructiveHint: false,
27
+ idempotentHint: true,
28
+ openWorldHint: false,
29
+ },
30
+ }, async (params, callContext) => {
31
+ const handlerContext = requestContextService.createRequestContext({
32
+ toolName: TOOL_NAME,
33
+ parentContext: callContext,
34
+ });
35
+ try {
36
+ const sessionId = getSessionId(handlerContext);
37
+ const result = await diffGitChanges(params, {
38
+ ...handlerContext,
39
+ getWorkingDirectory: () => getWorkingDirectory(sessionId),
45
40
  });
46
- const sessionId = _getSessionId(requestContext);
47
- const getWorkingDirectoryForSession = () => {
48
- return _getWorkingDirectory(sessionId);
49
- };
50
- const logicContext = {
51
- ...requestContext,
52
- sessionId: sessionId,
53
- getWorkingDirectory: getWorkingDirectoryForSession,
41
+ return {
42
+ structuredContent: result,
43
+ content: [{ type: "text", text: result.diff, contentType: "text/plain; charset=utf-8" }],
54
44
  };
55
- logger.info(`Executing tool: ${TOOL_NAME}`, logicContext);
56
- return await ErrorHandler.tryCatch(async () => {
57
- // Call the core logic function
58
- const diffResult = await diffGitChanges(validatedArgs, logicContext);
59
- // Format the result (the diff string) as plain text within TextContent
60
- const resultContent = {
61
- type: "text",
62
- // Return the raw diff output directly
63
- text: diffResult.diff,
64
- // Indicate the content type is plain text diff
65
- contentType: "text/plain; charset=utf-8", // Or 'text/x-diff'
66
- };
67
- logger.info(`Tool ${TOOL_NAME} executed successfully: ${diffResult.message}`, logicContext);
68
- // Success is determined by the logic function
69
- return { content: [resultContent] };
70
- }, {
71
- operation: toolOperation,
72
- context: logicContext,
73
- input: validatedArgs,
74
- errorCode: BaseErrorCode.INTERNAL_ERROR, // Default if unexpected error in logic
45
+ }
46
+ catch (error) {
47
+ logger.error(`Error in ${TOOL_NAME} handler`, { error, ...handlerContext });
48
+ const handledError = ErrorHandler.handleError(error, {
49
+ operation: `tool:${TOOL_NAME}`,
50
+ context: handlerContext,
51
+ input: params,
75
52
  });
76
- });
77
- logger.info(`Tool registered: ${TOOL_NAME}`, context);
78
- }, { operation, context, critical: true });
79
- }
53
+ const mcpError = handledError instanceof McpError
54
+ ? handledError
55
+ : new McpError(BaseErrorCode.INTERNAL_ERROR, "An unexpected error occurred.", { originalError: handledError });
56
+ return {
57
+ isError: true,
58
+ content: [{ type: "text", text: mcpError.message }],
59
+ structuredContent: mcpError.details,
60
+ };
61
+ }
62
+ });
63
+ logger.info(`Tool '${TOOL_NAME}' registered successfully.`, context);
64
+ };
@@ -1,6 +1,5 @@
1
1
  /**
2
2
  * @fileoverview Barrel file for the gitFetch tool.
3
+ * @module src/mcp-server/tools/gitFetch/index
3
4
  */
4
- export { registerGitFetchTool, initializeGitFetchStateAccessors, } from "./registration.js";
5
- // Export types if needed elsewhere, e.g.:
6
- // export type { GitFetchInput, GitFetchResult } from './logic.js';
5
+ export { registerGitFetchTool } from "./registration.js";
@@ -1,149 +1,67 @@
1
+ /**
2
+ * @fileoverview Defines the core logic, schemas, and types for the git_fetch tool.
3
+ * @module src/mcp-server/tools/gitFetch/logic
4
+ */
1
5
  import { execFile } from "child_process";
2
6
  import { promisify } from "util";
3
7
  import { z } from "zod";
4
- // Import utils from barrel (logger from ../utils/internal/logger.js)
5
- import { logger } from "../../../utils/index.js";
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
8
- // Import utils from barrel (sanitization from ../utils/security/sanitization.js)
9
- import { sanitization } from "../../../utils/index.js";
8
+ import { logger, sanitization } from "../../../utils/index.js";
9
+ import { McpError, BaseErrorCode } from "../../../types-global/errors.js";
10
10
  const execFileAsync = promisify(execFile);
11
- // Define the input schema for the git_fetch tool using Zod
11
+ // 1. DEFINE the Zod input schema.
12
12
  export const GitFetchInputSchema = z.object({
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
- remote: z
20
- .string()
21
- .optional()
22
- .describe("The remote repository to fetch from (e.g., 'origin'). If omitted, fetches from 'origin' or the default configured remote."),
23
- prune: z
24
- .boolean()
25
- .optional()
26
- .default(false)
27
- .describe("Before fetching, remove any remote-tracking references that no longer exist on the remote."),
28
- tags: z
29
- .boolean()
30
- .optional()
31
- .default(false)
32
- .describe("Fetch all tags from the remote (in addition to whatever else is fetched)."),
33
- all: z.boolean().optional().default(false).describe("Fetch all remotes."),
34
- // Add options like --depth, specific refspecs if needed
13
+ path: z.string().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
+ remote: z.string().optional().describe("The remote repository to fetch from (e.g., 'origin')."),
15
+ prune: z.boolean().default(false).describe("Remove remote-tracking references that no longer exist on the remote."),
16
+ tags: z.boolean().default(false).describe("Fetch all tags from the remote."),
17
+ all: z.boolean().default(false).describe("Fetch all remotes."),
18
+ });
19
+ // 2. DEFINE the Zod response schema.
20
+ export const GitFetchOutputSchema = z.object({
21
+ success: z.boolean().describe("Indicates if the command was successful."),
22
+ message: z.string().describe("A summary message of the result."),
35
23
  });
36
24
  /**
37
- * Executes the 'git fetch' command and returns structured JSON output.
38
- *
39
- * @param {GitFetchInput} input - The validated input object.
40
- * @param {RequestContext} context - The request context for logging and error handling.
41
- * @returns {Promise<GitFetchResult>} A promise that resolves with the structured fetch result.
42
- * @throws {McpError} Throws an McpError if path resolution, validation, or the git command fails unexpectedly.
25
+ * 4. IMPLEMENT the core logic function.
26
+ * @throws {McpError} If the logic encounters an unrecoverable issue.
43
27
  */
44
- export async function fetchGitRemote(input, context) {
28
+ export async function fetchGitRemote(params, context) {
45
29
  const operation = "fetchGitRemote";
46
- logger.debug(`Executing ${operation}`, { ...context, input });
47
- let targetPath;
48
- try {
49
- // Resolve and sanitize the target path
50
- if (input.path && input.path !== ".") {
51
- targetPath = input.path;
52
- }
53
- else {
54
- const workingDir = context.getWorkingDirectory();
55
- if (!workingDir) {
56
- throw new McpError(BaseErrorCode.VALIDATION_ERROR, "No path provided and no working directory set for the session.", { context, operation });
57
- }
58
- targetPath = workingDir;
59
- }
60
- targetPath = sanitization.sanitizePath(targetPath, {
61
- allowAbsolute: true,
62
- }).sanitizedPath;
63
- logger.debug("Sanitized path", {
64
- ...context,
65
- operation,
66
- sanitizedPath: targetPath,
67
- });
30
+ logger.debug(`Executing ${operation}`, { ...context, params });
31
+ const workingDir = context.getWorkingDirectory();
32
+ if (params.path === "." && !workingDir) {
33
+ throw new McpError(BaseErrorCode.VALIDATION_ERROR, "No session working directory set. Please specify a 'path' or use 'git_set_working_dir' first.");
68
34
  }
69
- catch (error) {
70
- logger.error("Path resolution or sanitization failed", {
71
- ...context,
72
- operation,
73
- error,
74
- });
75
- if (error instanceof McpError)
76
- throw error;
77
- throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Invalid path: ${error instanceof Error ? error.message : String(error)}`, { context, operation, originalError: error });
35
+ const targetPath = sanitization.sanitizePath(params.path === "." ? workingDir : params.path, { allowAbsolute: true }).sanitizedPath;
36
+ const args = ["-C", targetPath, "fetch"];
37
+ if (params.prune)
38
+ args.push("--prune");
39
+ if (params.tags)
40
+ args.push("--tags");
41
+ if (params.all) {
42
+ args.push("--all");
43
+ }
44
+ else if (params.remote) {
45
+ args.push(params.remote);
78
46
  }
79
47
  try {
80
- // Construct the git fetch command
81
- const args = ["-C", targetPath, "fetch"];
82
- if (input.prune) {
83
- args.push("--prune");
84
- }
85
- if (input.tags) {
86
- args.push("--tags");
87
- }
88
- if (input.all) {
89
- args.push("--all");
90
- }
91
- else if (input.remote) {
92
- args.push(input.remote); // Fetch specific remote if 'all' is not used
93
- }
94
- // If neither 'all' nor 'remote' is specified, git fetch defaults to 'origin' or configured upstream.
95
- logger.debug(`Executing command: git ${args.join(" ")}`, {
96
- ...context,
97
- operation,
98
- });
99
- // Execute command. Fetch output is primarily on stderr.
100
- const { stdout, stderr } = await execFileAsync("git", args);
101
- logger.debug(`Git fetch stdout: ${stdout}`, { ...context, operation }); // stdout is usually empty
102
- if (stderr) {
103
- logger.debug(`Git fetch stderr: ${stderr}`, { ...context, operation }); // stderr contains fetch details
104
- }
105
- // Analyze stderr for success/summary
106
- const message = "Fetch successful.";
107
- const summary = stderr.trim() || "No changes detected.";
108
- logger.info(message, {
109
- ...context,
110
- operation,
111
- path: targetPath,
112
- summary,
113
- });
114
- return { success: true, message, summary };
48
+ logger.debug(`Executing command: git ${args.join(" ")}`, { ...context, operation });
49
+ const { stderr } = await execFileAsync("git", args);
50
+ const message = stderr.trim() || "Fetch successful.";
51
+ return { success: true, message };
115
52
  }
116
53
  catch (error) {
117
- logger.error(`Failed to execute git fetch command`, {
118
- ...context,
119
- operation,
120
- path: targetPath,
121
- error: error.message,
122
- stderr: error.stderr,
123
- stdout: error.stdout,
124
- });
125
- const errorMessage = error.stderr || error.stdout || error.message || "";
126
- // Handle specific error cases
54
+ const errorMessage = error.stderr || error.message || "";
55
+ logger.error(`Failed to execute git fetch command`, { ...context, operation, errorMessage });
127
56
  if (errorMessage.toLowerCase().includes("not a git repository")) {
128
- throw new McpError(BaseErrorCode.NOT_FOUND, `Path is not a Git repository: ${targetPath}`, { context, operation, originalError: error });
129
- }
130
- if (errorMessage.includes("resolve host") ||
131
- errorMessage.includes("Could not read from remote repository") ||
132
- errorMessage.includes("Connection timed out")) {
133
- throw new McpError(BaseErrorCode.SERVICE_UNAVAILABLE, `Failed to connect to remote repository '${input.remote || "default"}'. Error: ${errorMessage}`, { context, operation, originalError: error });
134
- }
135
- if (errorMessage.includes("fatal: ") &&
136
- errorMessage.includes("couldn't find remote ref")) {
137
- throw new McpError(BaseErrorCode.NOT_FOUND, `Remote ref not found. Error: ${errorMessage}`, { context, operation, originalError: error });
57
+ throw new McpError(BaseErrorCode.NOT_FOUND, `Path is not a Git repository: ${targetPath}`);
138
58
  }
139
- if (errorMessage.includes("Authentication failed") ||
140
- errorMessage.includes("Permission denied")) {
141
- throw new McpError(BaseErrorCode.UNAUTHORIZED, `Authentication failed for remote repository '${input.remote || "default"}'. Error: ${errorMessage}`, { context, operation, originalError: error });
59
+ if (errorMessage.includes("Could not read from remote repository")) {
60
+ throw new McpError(BaseErrorCode.SERVICE_UNAVAILABLE, `Failed to connect to remote repository '${params.remote || "default"}'.`);
142
61
  }
143
- if (errorMessage.includes("does not appear to be a git repository")) {
144
- throw new McpError(BaseErrorCode.NOT_FOUND, `Remote '${input.remote || "default"}' does not appear to be a git repository. Error: ${errorMessage}`, { context, operation, originalError: error });
62
+ if (errorMessage.includes("Authentication failed")) {
63
+ throw new McpError(BaseErrorCode.UNAUTHORIZED, `Authentication failed for remote repository '${params.remote || "default"}'.`);
145
64
  }
146
- // Generic internal error for other failures
147
- throw new McpError(BaseErrorCode.INTERNAL_ERROR, `Failed to git fetch for path: ${targetPath}. Error: ${errorMessage}`, { context, operation, originalError: error });
65
+ throw new McpError(BaseErrorCode.INTERNAL_ERROR, `Git fetch failed: ${errorMessage}`);
148
66
  }
149
67
  }
@@ -1,76 +1,64 @@
1
- // Import utils from barrel (ErrorHandler from ../utils/internal/errorHandler.js)
2
- import { ErrorHandler } from "../../../utils/index.js";
3
- // Import utils from barrel (logger from ../utils/internal/logger.js)
4
- import { logger } from "../../../utils/index.js";
5
- // Import utils from barrel (requestContextService, RequestContext 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 { fetchGitRemote, GitFetchInputSchema, } from "./logic.js";
9
- let _getWorkingDirectory;
10
- let _getSessionId;
11
1
  /**
12
- * Initializes the state accessors needed by the tool registration.
13
- * This should be called once during server setup.
14
- * @param getWdFn - Function to get the working directory for a session.
15
- * @param getSidFn - Function to get the session ID from context.
2
+ * @fileoverview Handles registration and error handling for the git_fetch tool.
3
+ * @module src/mcp-server/tools/gitFetch/registration
16
4
  */
17
- export function initializeGitFetchStateAccessors(getWdFn, getSidFn) {
18
- _getWorkingDirectory = getWdFn;
19
- _getSessionId = getSidFn;
20
- logger.info("State accessors initialized for git_fetch tool registration.");
21
- }
5
+ import { ErrorHandler, logger, requestContextService } from "../../../utils/index.js";
6
+ import { McpError, BaseErrorCode } from "../../../types-global/errors.js";
7
+ import { fetchGitRemote, GitFetchInputSchema, GitFetchOutputSchema, } from "./logic.js";
22
8
  const TOOL_NAME = "git_fetch";
23
- const TOOL_DESCRIPTION = "Downloads objects and refs from one or more other repositories. Can fetch specific remotes or all, prune stale branches, and fetch tags.";
9
+ const TOOL_DESCRIPTION = "Downloads objects and refs from one or more repositories. Can fetch specific remotes or all, prune stale branches, and fetch tags.";
24
10
  /**
25
- * Registers the git_fetch tool with the MCP server.
26
- *
27
- * @param {McpServer} server - The MCP server instance.
28
- * @throws {Error} If state accessors are not initialized.
11
+ * Registers the git_fetch tool with the MCP server instance.
12
+ * @param server The MCP server instance.
13
+ * @param getWorkingDirectory Function to get the session's working directory.
14
+ * @param getSessionId Function to get the session ID from context.
29
15
  */
30
- export async function registerGitFetchTool(server) {
31
- if (!_getWorkingDirectory || !_getSessionId) {
32
- throw new Error("State accessors for git_fetch must be initialized before registration.");
33
- }
16
+ export const registerGitFetchTool = async (server, getWorkingDirectory, getSessionId) => {
34
17
  const operation = "registerGitFetchTool";
35
18
  const context = requestContextService.createRequestContext({ operation });
36
- await ErrorHandler.tryCatch(async () => {
37
- server.tool(TOOL_NAME, TOOL_DESCRIPTION, GitFetchInputSchema.shape, // Provide the Zod schema shape
38
- async (validatedArgs, callContext) => {
39
- const toolOperation = "tool:git_fetch";
40
- const requestContext = requestContextService.createRequestContext({
41
- operation: toolOperation,
42
- parentContext: callContext,
19
+ server.registerTool(TOOL_NAME, {
20
+ title: "Git Fetch",
21
+ description: TOOL_DESCRIPTION,
22
+ inputSchema: GitFetchInputSchema.shape,
23
+ outputSchema: GitFetchOutputSchema.shape,
24
+ annotations: {
25
+ readOnlyHint: false, // Modifies local refs
26
+ destructiveHint: false,
27
+ idempotentHint: false,
28
+ openWorldHint: true, // Interacts with remote repositories
29
+ },
30
+ }, async (params, callContext) => {
31
+ const handlerContext = requestContextService.createRequestContext({
32
+ toolName: TOOL_NAME,
33
+ parentContext: callContext,
34
+ });
35
+ try {
36
+ const sessionId = getSessionId(handlerContext);
37
+ const result = await fetchGitRemote(params, {
38
+ ...handlerContext,
39
+ getWorkingDirectory: () => getWorkingDirectory(sessionId),
43
40
  });
44
- const sessionId = _getSessionId(requestContext);
45
- const getWorkingDirectoryForSession = () => {
46
- return _getWorkingDirectory(sessionId);
47
- };
48
- const logicContext = {
49
- ...requestContext,
50
- sessionId: sessionId,
51
- getWorkingDirectory: getWorkingDirectoryForSession,
41
+ return {
42
+ structuredContent: result,
43
+ content: [{ type: "text", text: `Success: ${JSON.stringify(result, null, 2)}` }],
52
44
  };
53
- logger.info(`Executing tool: ${TOOL_NAME}`, logicContext);
54
- return await ErrorHandler.tryCatch(async () => {
55
- // Call the core logic function
56
- const fetchResult = await fetchGitRemote(validatedArgs, logicContext);
57
- // Format the result as a JSON string within TextContent
58
- const resultContent = {
59
- type: "text",
60
- // Stringify the entire GitFetchResult object
61
- text: JSON.stringify(fetchResult, null, 2), // Pretty-print JSON
62
- contentType: "application/json",
63
- };
64
- logger.info(`Tool ${TOOL_NAME} executed successfully: ${fetchResult.message}`, logicContext);
65
- // Success is determined by the logic function and included in the result object
66
- return { content: [resultContent] };
67
- }, {
68
- operation: toolOperation,
69
- context: logicContext,
70
- input: validatedArgs,
71
- errorCode: BaseErrorCode.INTERNAL_ERROR, // Default if unexpected error in logic
45
+ }
46
+ catch (error) {
47
+ logger.error(`Error in ${TOOL_NAME} handler`, { error, ...handlerContext });
48
+ const handledError = ErrorHandler.handleError(error, {
49
+ operation: `tool:${TOOL_NAME}`,
50
+ context: handlerContext,
51
+ input: params,
72
52
  });
73
- });
74
- logger.info(`Tool registered: ${TOOL_NAME}`, context);
75
- }, { operation, context, critical: true });
76
- }
53
+ const mcpError = handledError instanceof McpError
54
+ ? handledError
55
+ : new McpError(BaseErrorCode.INTERNAL_ERROR, "An unexpected error occurred.", { originalError: handledError });
56
+ return {
57
+ isError: true,
58
+ content: [{ type: "text", text: mcpError.message }],
59
+ structuredContent: mcpError.details,
60
+ };
61
+ }
62
+ });
63
+ logger.info(`Tool '${TOOL_NAME}' registered successfully.`, context);
64
+ };
@@ -1,7 +1,5 @@
1
1
  /**
2
- * @fileoverview Barrel file for the git_init tool.
3
- * Exports the registration function and the state accessor initializer.
2
+ * @fileoverview Barrel file for the gitInit tool.
3
+ * @module src/mcp-server/tools/gitInit/index
4
4
  */
5
- export { registerGitInitTool, initializeGitInitStateAccessors, } from "./registration.js";
6
- // Export types if needed elsewhere, e.g.:
7
- // export type { GitInitInput, GitInitResult } from './logic.js';
5
+ export { registerGitInitTool } from "./registration.js";