@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,71 +1,64 @@
1
- import { BaseErrorCode } from "../../../types-global/errors.js"; // Direct import for types-global
2
- import { ErrorHandler, logger, requestContextService, } from "../../../utils/index.js"; // ErrorHandler (./utils/internal/errorHandler.js), logger (./utils/internal/logger.js), requestContextService & RequestContext (./utils/internal/requestContext.js)
3
- import { GitResetInputSchema, resetGitState, } from "./logic.js";
4
- let _getWorkingDirectory;
5
- let _getSessionId;
6
1
  /**
7
- * Initializes the state accessors needed by the tool registration.
8
- * This should be called once during server setup.
9
- * @param getWdFn - Function to get the working directory for a session.
10
- * @param getSidFn - Function to get the session ID from context.
2
+ * @fileoverview Handles registration and error handling for the git_reset tool.
3
+ * @module src/mcp-server/tools/gitReset/registration
11
4
  */
12
- export function initializeGitResetStateAccessors(getWdFn, getSidFn) {
13
- _getWorkingDirectory = getWdFn;
14
- _getSessionId = getSidFn;
15
- logger.info("State accessors initialized for git_reset tool registration.");
16
- }
5
+ import { ErrorHandler, logger, requestContextService } from "../../../utils/index.js";
6
+ import { McpError, BaseErrorCode } from "../../../types-global/errors.js";
7
+ import { resetGitState, GitResetInputSchema, GitResetOutputSchema, } from "./logic.js";
17
8
  const TOOL_NAME = "git_reset";
18
9
  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.";
19
10
  /**
20
- * Registers the git_reset tool with the MCP server.
21
- *
22
- * @param {McpServer} server - The MCP server instance.
23
- * @throws {Error} If state accessors are not initialized.
11
+ * Registers the git_reset 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.
24
15
  */
25
- export async function registerGitResetTool(server) {
26
- if (!_getWorkingDirectory || !_getSessionId) {
27
- throw new Error("State accessors for git_reset must be initialized before registration.");
28
- }
16
+ export const registerGitResetTool = async (server, getWorkingDirectory, getSessionId) => {
29
17
  const operation = "registerGitResetTool";
30
18
  const context = requestContextService.createRequestContext({ operation });
31
- await ErrorHandler.tryCatch(async () => {
32
- server.tool(TOOL_NAME, TOOL_DESCRIPTION, GitResetInputSchema.shape, // Provide the Zod schema shape
33
- async (validatedArgs, callContext) => {
34
- const toolOperation = "tool:git_reset";
35
- const requestContext = requestContextService.createRequestContext({
36
- operation: toolOperation,
37
- parentContext: callContext,
19
+ server.registerTool(TOOL_NAME, {
20
+ title: "Git Reset",
21
+ description: TOOL_DESCRIPTION,
22
+ inputSchema: GitResetInputSchema.shape,
23
+ outputSchema: GitResetOutputSchema.shape,
24
+ annotations: {
25
+ readOnlyHint: false,
26
+ destructiveHint: true, // Can be very destructive, especially in 'hard' mode
27
+ idempotentHint: false,
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 resetGitState(params, {
38
+ ...handlerContext,
39
+ getWorkingDirectory: () => getWorkingDirectory(sessionId),
38
40
  });
39
- const sessionId = _getSessionId(requestContext);
40
- const getWorkingDirectoryForSession = () => {
41
- return _getWorkingDirectory(sessionId);
42
- };
43
- const logicContext = {
44
- ...requestContext,
45
- sessionId: sessionId,
46
- getWorkingDirectory: getWorkingDirectoryForSession,
41
+ return {
42
+ structuredContent: result,
43
+ content: [{ type: "text", text: `Success: ${JSON.stringify(result, null, 2)}` }],
47
44
  };
48
- logger.info(`Executing tool: ${TOOL_NAME}`, logicContext);
49
- return await ErrorHandler.tryCatch(async () => {
50
- // Call the core logic function
51
- const resetResult = await resetGitState(validatedArgs, logicContext);
52
- // Format the result as a JSON string within TextContent
53
- const resultContent = {
54
- type: "text",
55
- // Stringify the entire GitResetResult object
56
- text: JSON.stringify(resetResult, null, 2), // Pretty-print JSON
57
- contentType: "application/json",
58
- };
59
- logger.info(`Tool ${TOOL_NAME} executed successfully: ${resetResult.message}`, logicContext);
60
- // Success is determined by the logic function and included in the result object
61
- return { content: [resultContent] };
62
- }, {
63
- operation: toolOperation,
64
- context: logicContext,
65
- input: validatedArgs,
66
- 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,
67
52
  });
68
- });
69
- logger.info(`Tool registered: ${TOOL_NAME}`, context);
70
- }, { operation, context, critical: true });
71
- }
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_set_working_dir tool.
3
- * Exports the registration function and potentially other related components.
2
+ * @fileoverview Barrel file for the gitSetWorkingDir tool.
3
+ * @module src/mcp-server/tools/gitSetWorkingDir/index
4
4
  */
5
- export { registerGitSetWorkingDirTool, initializeGitSetWorkingDirStateAccessors, } from "./registration.js";
6
- // Export types if needed elsewhere, e.g.:
7
- // export type { GitSetWorkingDirInput, GitSetWorkingDirResult } from './logic.js';
5
+ export { registerGitSetWorkingDirTool } from "./registration.js";
@@ -1,159 +1,71 @@
1
+ /**
2
+ * @fileoverview Defines the core logic, schemas, and types for the git_set_working_dir tool.
3
+ * @module src/mcp-server/tools/gitSetWorkingDir/logic
4
+ */
1
5
  import { execFile } from "child_process";
2
6
  import fs from "fs/promises";
3
7
  import { promisify } from "util";
4
8
  import { z } from "zod";
5
- import { BaseErrorCode, McpError } from "../../../types-global/errors.js"; // Direct import for types-global
6
- import { logger, sanitization } from "../../../utils/index.js"; // RequestContext (./utils/internal/requestContext.js), logger (./utils/internal/logger.js), sanitization (./utils/security/sanitization.js)
9
+ import { logger, sanitization } from "../../../utils/index.js";
10
+ import { McpError, BaseErrorCode } from "../../../types-global/errors.js";
7
11
  const execFileAsync = promisify(execFile);
8
- // Define the Zod schema for input validation
12
+ // 1. DEFINE the Zod input schema.
9
13
  export const GitSetWorkingDirInputSchema = z.object({
10
- path: z
11
- .string()
12
- .min(1, "Path cannot be empty.")
13
- .describe("The absolute path to set as the default working directory for the current session. Set this before using other git_* tools."),
14
- validateGitRepo: z
15
- .boolean()
16
- .default(true)
17
- .describe("Whether to validate that the path is a Git repository"),
18
- initializeIfNotPresent: z
19
- .boolean()
20
- .optional()
21
- .default(false)
22
- .describe("If true and the directory is not a Git repository, attempt to initialize it with 'git init'."),
14
+ path: z.string().min(1).describe("The absolute path to set as the default working directory."),
15
+ validateGitRepo: z.boolean().default(true).describe("Validate that the path is a Git repository."),
16
+ initializeIfNotPresent: z.boolean().default(false).describe("If not a Git repository, initialize it with 'git init'."),
23
17
  });
24
- /**
25
- * Logic for the git_set_working_dir tool.
26
- * Sets a global working directory path for the current session.
27
- * Validates the path and optionally checks if it's a Git repository.
28
- *
29
- * @param {GitSetWorkingDirInput} input - The validated input arguments.
30
- * @param {RequestContext} context - The request context, potentially containing session ID.
31
- * @returns {Promise<GitSetWorkingDirResult>} The result of the operation.
32
- * @throws {McpError} Throws McpError for validation failures or operational errors.
33
- */
34
- export async function gitSetWorkingDirLogic(input, context) {
35
- const operation = "gitSetWorkingDirLogic";
36
- logger.debug(`Executing ${operation}`, { ...context, input });
37
- let sanitizedPath;
18
+ // 2. DEFINE the Zod response schema.
19
+ export const GitSetWorkingDirOutputSchema = z.object({
20
+ success: z.boolean().describe("Indicates if the command was successful."),
21
+ message: z.string().describe("A summary message of the result."),
22
+ path: z.string().describe("The path that was set as the working directory."),
23
+ initialized: z.boolean().describe("Indicates if a new repository was initialized."),
24
+ });
25
+ async function checkIsGitRepo(path) {
38
26
  try {
39
- // Sanitize the path. Must explicitly allow absolute paths for this tool.
40
- // It normalizes and checks for traversal issues.
41
- sanitizedPath = sanitization.sanitizePath(input.path, {
42
- allowAbsolute: true,
43
- }).sanitizedPath;
44
- logger.debug(`Sanitized path: ${sanitizedPath}`, { ...context, operation });
27
+ const { stdout } = await execFileAsync("git", ["rev-parse", "--is-inside-work-tree"], { cwd: path });
28
+ return stdout.trim() === "true";
45
29
  }
46
- catch (error) {
47
- logger.error("Path sanitization failed", error, { ...context, operation });
48
- if (error instanceof McpError)
49
- throw error;
50
- throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Invalid path provided: ${error.message}`, { context, operation });
30
+ catch {
31
+ return false;
51
32
  }
52
- // Check if the directory exists
33
+ }
34
+ /**
35
+ * 4. IMPLEMENT the core logic function.
36
+ * @throws {McpError} If the logic encounters an unrecoverable issue.
37
+ */
38
+ export async function gitSetWorkingDirLogic(params, context) {
39
+ const operation = "gitSetWorkingDirLogic";
40
+ logger.debug(`Executing ${operation}`, { ...context, params });
41
+ const sanitizedPath = sanitization.sanitizePath(params.path, { allowAbsolute: true }).sanitizedPath;
53
42
  try {
54
43
  const stats = await fs.stat(sanitizedPath);
55
44
  if (!stats.isDirectory()) {
56
- throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Path is not a directory: ${sanitizedPath}`, { context, operation });
45
+ throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Path is not a directory: ${sanitizedPath}`);
57
46
  }
58
47
  }
59
48
  catch (error) {
60
- if (error.code === "ENOENT") {
61
- throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Directory does not exist: ${sanitizedPath}`, { context, operation });
62
- }
63
- logger.error("Failed to stat directory", error, {
64
- ...context,
65
- operation,
66
- path: sanitizedPath,
67
- });
68
- throw new McpError(BaseErrorCode.INTERNAL_ERROR, `Failed to access path: ${error.message}`, { context, operation });
49
+ if (error instanceof McpError)
50
+ throw error;
51
+ throw new McpError(BaseErrorCode.NOT_FOUND, `Directory does not exist or is inaccessible: ${sanitizedPath}`);
69
52
  }
70
- let isGitRepo = false;
53
+ let isGitRepo = await checkIsGitRepo(sanitizedPath);
71
54
  let initializedRepo = false;
72
- try {
73
- const { stdout } = await execFileAsync("git", ["rev-parse", "--is-inside-work-tree"], {
74
- cwd: sanitizedPath,
75
- });
76
- if (stdout.trim() === "true") {
77
- isGitRepo = true;
78
- logger.debug("Path is already a Git repository", {
79
- ...context,
80
- operation,
81
- path: sanitizedPath,
82
- });
83
- }
84
- }
85
- catch (error) {
86
- logger.debug("Path is not a Git repository (rev-parse failed or returned non-true)", {
87
- ...context,
88
- operation,
89
- path: sanitizedPath,
90
- error: error.message,
91
- });
92
- isGitRepo = false;
93
- }
94
- if (!isGitRepo && input.initializeIfNotPresent) {
95
- logger.info(`Path is not a Git repository. Attempting to initialize (initializeIfNotPresent=true) with initial branch 'main'.`, { ...context, operation, path: sanitizedPath });
55
+ if (!isGitRepo && params.initializeIfNotPresent) {
96
56
  try {
97
- await execFileAsync("git", ["init", "--initial-branch=main"], {
98
- cwd: sanitizedPath,
99
- });
57
+ await execFileAsync("git", ["init", "--initial-branch=main"], { cwd: sanitizedPath });
100
58
  initializedRepo = true;
101
- isGitRepo = true; // Now it is a git repo
102
- logger.info('Successfully initialized Git repository with initial branch "main".', { ...context, operation, path: sanitizedPath });
59
+ isGitRepo = true;
103
60
  }
104
61
  catch (initError) {
105
- logger.error("Failed to initialize Git repository", initError, {
106
- ...context,
107
- operation,
108
- path: sanitizedPath,
109
- });
110
- throw new McpError(BaseErrorCode.INTERNAL_ERROR, `Failed to initialize Git repository at ${sanitizedPath}: ${initError.message}`, { context, operation });
62
+ throw new McpError(BaseErrorCode.INTERNAL_ERROR, `Failed to initialize Git repository: ${initError.message}`);
111
63
  }
112
64
  }
113
- // After potential initialization, if validateGitRepo is true, it must now be a Git repo.
114
- if (input.validateGitRepo && !isGitRepo) {
115
- logger.warning("Path is not a valid Git repository and initialization was not performed or failed.", { ...context, operation, path: sanitizedPath });
116
- throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Path is not a valid Git repository: ${sanitizedPath}.`, { context, operation });
117
- }
118
- // --- Update Session State ---
119
- // This part needs access to the session state mechanism defined in server.ts
120
- // We assume the context provides a way to set the working directory for the current session.
121
- try {
122
- context.setWorkingDirectory(sanitizedPath);
123
- const message = `Working directory set for session ${context.sessionId || "stdio"} to: ${sanitizedPath}`;
124
- logger.info(message, { ...context, operation });
125
- }
126
- catch (error) {
127
- logger.error("Failed to set working directory in session state", error, {
128
- ...context,
129
- operation,
130
- });
131
- // This indicates an internal logic error in how state is passed/managed.
132
- throw new McpError(BaseErrorCode.INTERNAL_ERROR, "Failed to update session state.", { context, operation });
133
- }
134
- let message = `Working directory set to: ${sanitizedPath}`;
135
- if (initializedRepo) {
136
- message += " (New Git repository initialized).";
137
- }
138
- else if (isGitRepo && input.validateGitRepo) {
139
- // Only state "Existing" if validation was on and it passed
140
- message += " (Existing Git repository).";
141
- }
142
- else if (isGitRepo && !input.validateGitRepo) {
143
- // It is a git repo, but we weren't asked to validate it
144
- message += " (Is a Git repository, validation skipped).";
145
- }
146
- else if (!isGitRepo &&
147
- !input.validateGitRepo &&
148
- !input.initializeIfNotPresent) {
149
- // Not a git repo, validation off, no init request
150
- message +=
151
- " (Not a Git repository, validation skipped, no initialization requested).";
65
+ if (params.validateGitRepo && !isGitRepo) {
66
+ throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Path is not a valid Git repository: ${sanitizedPath}.`);
152
67
  }
153
- return {
154
- success: true,
155
- message: message,
156
- path: sanitizedPath,
157
- initialized: initializedRepo,
158
- };
68
+ context.setWorkingDirectory(sanitizedPath);
69
+ const message = `Working directory set to: ${sanitizedPath}${initializedRepo ? " (New repository initialized)." : ""}`;
70
+ return { success: true, message, path: sanitizedPath, initialized: initializedRepo };
159
71
  }
@@ -1,94 +1,64 @@
1
- import { BaseErrorCode } from "../../../types-global/errors.js"; // Direct import for types-global
2
- import { ErrorHandler, logger, requestContextService, } from "../../../utils/index.js"; // ErrorHandler (./utils/internal/errorHandler.js), logger (./utils/internal/logger.js), requestContextService & RequestContext (./utils/internal/requestContext.js)
3
- import { GitSetWorkingDirInputSchema, gitSetWorkingDirLogic, } from "./logic.js";
4
- let _getWorkingDirectory; // Added getter
5
- let _setWorkingDirectory;
6
- let _getSessionId;
7
1
  /**
8
- * Initializes the state accessors needed by the tool registration.
9
- * This should be called once during server setup.
10
- * @param getWdFn - Function to get the working directory for a session.
11
- * @param setWdFn - Function to set the working directory for a session.
12
- * @param getSidFn - Function to get the session ID from context.
2
+ * @fileoverview Handles registration and error handling for the git_set_working_dir tool.
3
+ * @module src/mcp-server/tools/gitSetWorkingDir/registration
13
4
  */
14
- export function initializeGitSetWorkingDirStateAccessors(getWdFn, // Added getter parameter
15
- setWdFn, getSidFn) {
16
- _getWorkingDirectory = getWdFn; // Store getter
17
- _setWorkingDirectory = setWdFn;
18
- _getSessionId = getSidFn;
19
- logger.info("State accessors initialized for git_set_working_dir tool registration.");
20
- }
5
+ import { ErrorHandler, logger, requestContextService } from "../../../utils/index.js";
6
+ import { McpError, BaseErrorCode } from "../../../types-global/errors.js";
7
+ import { gitSetWorkingDirLogic, GitSetWorkingDirInputSchema, GitSetWorkingDirOutputSchema, } from "./logic.js";
21
8
  const TOOL_NAME = "git_set_working_dir";
22
9
  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.";
23
10
  /**
24
- * Registers the git_set_working_dir tool with the MCP server.
25
- *
26
- * @param {McpServer} server - The MCP server instance.
27
- * @throws {Error} If state accessors are not initialized.
11
+ * Registers the git_set_working_dir tool with the MCP server instance.
12
+ * @param server The MCP server instance.
13
+ * @param setWorkingDirectory Function to set the session's working directory.
14
+ * @param getSessionId Function to get the session ID from context.
28
15
  */
29
- export async function registerGitSetWorkingDirTool(server) {
30
- // Check all required accessors
31
- if (!_getWorkingDirectory || !_setWorkingDirectory || !_getSessionId) {
32
- throw new Error("State accessors (getWD, setWD, getSID) for git_set_working_dir must be initialized before registration.");
33
- }
34
- try {
35
- server.tool(TOOL_NAME, TOOL_DESCRIPTION, GitSetWorkingDirInputSchema.shape, // Pass the shape for SDK validation
36
- async (validatedArgs, callContext) => {
37
- // Use callContext provided by SDK
38
- const operation = "tool:git_set_working_dir";
39
- // Create a request context, potentially inheriting from callContext if it provides relevant info
40
- const requestContext = requestContextService.createRequestContext({
41
- operation,
42
- parentContext: callContext,
16
+ export const registerGitSetWorkingDirTool = async (server, setWorkingDirectory, getSessionId) => {
17
+ const operation = "registerGitSetWorkingDirTool";
18
+ const context = requestContextService.createRequestContext({ operation });
19
+ server.registerTool(TOOL_NAME, {
20
+ title: "Git Set Working Directory",
21
+ description: TOOL_DESCRIPTION,
22
+ inputSchema: GitSetWorkingDirInputSchema.shape,
23
+ outputSchema: GitSetWorkingDirOutputSchema.shape,
24
+ annotations: {
25
+ readOnlyHint: true, // Modifies session state, but not external files
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 gitSetWorkingDirLogic(params, {
38
+ ...handlerContext,
39
+ setWorkingDirectory: (path) => setWorkingDirectory(sessionId, path),
43
40
  });
44
- // Get session ID using the accessor function
45
- const sessionId = _getSessionId(requestContext); // Non-null assertion as we checked initialization
46
- // Define the session-specific setter function
47
- const setWorkingDirectoryForSession = (path) => {
48
- _setWorkingDirectory(sessionId, path); // Non-null assertion
49
- };
50
- // Define the session-specific getter function (needed by logic?)
51
- // If the logic needs the current WD, pass the getter too. Assuming it might.
52
- const getWorkingDirectoryForSession = () => {
53
- return _getWorkingDirectory(sessionId); // Non-null assertion
54
- };
55
- // Enhance context with session ID and the getter/setter functions
56
- const logicContext = {
57
- ...requestContext,
58
- sessionId: sessionId,
59
- getWorkingDirectory: getWorkingDirectoryForSession, // Pass getter
60
- setWorkingDirectory: setWorkingDirectoryForSession, // Pass setter
41
+ return {
42
+ structuredContent: result,
43
+ content: [{ type: "text", text: `Success: ${JSON.stringify(result, null, 2)}` }],
61
44
  };
62
- return await ErrorHandler.tryCatch(async () => {
63
- // Call the core logic function with validated args and enhanced context
64
- const result = await gitSetWorkingDirLogic(validatedArgs, logicContext);
65
- // Format the successful result for the MCP client
66
- const responseContent = {
67
- type: "text",
68
- text: JSON.stringify(result, null, 2), // Pretty-print JSON result
69
- contentType: "application/json",
70
- };
71
- logger.info(`Tool ${TOOL_NAME} executed successfully`, {
72
- ...logicContext,
73
- result,
74
- });
75
- return { content: [responseContent] };
76
- }, {
77
- operation,
78
- context: logicContext,
79
- input: validatedArgs, // Log sanitized input
80
- errorCode: BaseErrorCode.INTERNAL_ERROR, // Default error code if logic fails unexpectedly
81
- // toolName: TOOL_NAME, // Removed as it's not part of ErrorHandlerOptions
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,
82
52
  });
83
- });
84
- logger.info(`Tool registered: ${TOOL_NAME}`);
85
- }
86
- catch (error) {
87
- logger.error(`Failed to register tool: ${TOOL_NAME}`, {
88
- error: error instanceof Error ? error.message : String(error),
89
- stack: error instanceof Error ? error.stack : undefined,
90
- });
91
- // Propagate the error to prevent server startup if registration fails
92
- throw error;
93
- }
94
- }
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_show tool.
3
- * Exports the registration function and state accessor initialization function.
2
+ * @fileoverview Barrel file for the gitShow tool.
3
+ * @module src/mcp-server/tools/gitShow/index
4
4
  */
5
- export { registerGitShowTool, initializeGitShowStateAccessors, } from "./registration.js";
6
- // Export types if needed elsewhere, e.g.:
7
- // export type { GitShowInput, GitShowResult } from './logic.js';
5
+ export { registerGitShowTool } from "./registration.js";