@cyanheads/git-mcp-server 2.1.7 → 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.
- package/README.md +4 -4
- package/dist/mcp-server/server.js +69 -228
- package/dist/mcp-server/tools/gitAdd/index.js +2 -4
- package/dist/mcp-server/tools/gitAdd/logic.js +17 -74
- package/dist/mcp-server/tools/gitAdd/registration.js +38 -59
- package/dist/mcp-server/tools/gitBranch/index.js +3 -5
- package/dist/mcp-server/tools/gitBranch/logic.js +118 -296
- package/dist/mcp-server/tools/gitBranch/registration.js +52 -66
- package/dist/mcp-server/tools/gitCheckout/index.js +2 -3
- package/dist/mcp-server/tools/gitCheckout/logic.js +47 -122
- package/dist/mcp-server/tools/gitCheckout/registration.js +53 -72
- package/dist/mcp-server/tools/gitCherryPick/index.js +3 -5
- package/dist/mcp-server/tools/gitCherryPick/logic.js +55 -162
- package/dist/mcp-server/tools/gitCherryPick/registration.js +52 -67
- package/dist/mcp-server/tools/gitClean/index.js +3 -5
- package/dist/mcp-server/tools/gitClean/logic.js +44 -143
- package/dist/mcp-server/tools/gitClean/registration.js +52 -92
- package/dist/mcp-server/tools/gitClearWorkingDir/index.js +3 -5
- package/dist/mcp-server/tools/gitClearWorkingDir/logic.js +19 -26
- package/dist/mcp-server/tools/gitClearWorkingDir/registration.js +55 -73
- package/dist/mcp-server/tools/gitClone/index.js +2 -4
- package/dist/mcp-server/tools/gitClone/logic.js +50 -171
- package/dist/mcp-server/tools/gitClone/registration.js +51 -42
- package/dist/mcp-server/tools/gitCommit/index.js +2 -4
- package/dist/mcp-server/tools/gitCommit/logic.js +90 -295
- package/dist/mcp-server/tools/gitCommit/registration.js +52 -73
- package/dist/mcp-server/tools/gitDiff/index.js +2 -3
- package/dist/mcp-server/tools/gitDiff/logic.js +78 -254
- package/dist/mcp-server/tools/gitDiff/registration.js +53 -68
- package/dist/mcp-server/tools/gitFetch/index.js +2 -3
- package/dist/mcp-server/tools/gitFetch/logic.js +47 -129
- package/dist/mcp-server/tools/gitFetch/registration.js +54 -66
- package/dist/mcp-server/tools/gitInit/index.js +3 -5
- package/dist/mcp-server/tools/gitInit/logic.js +46 -152
- package/dist/mcp-server/tools/gitInit/registration.js +52 -104
- package/dist/mcp-server/tools/gitLog/index.js +2 -3
- package/dist/mcp-server/tools/gitLog/logic.js +75 -257
- package/dist/mcp-server/tools/gitLog/registration.js +54 -66
- package/dist/mcp-server/tools/gitMerge/index.js +3 -5
- package/dist/mcp-server/tools/gitMerge/logic.js +52 -179
- package/dist/mcp-server/tools/gitMerge/registration.js +52 -71
- package/dist/mcp-server/tools/gitPull/index.js +2 -3
- package/dist/mcp-server/tools/gitPull/logic.js +48 -146
- package/dist/mcp-server/tools/gitPull/registration.js +53 -75
- package/dist/mcp-server/tools/gitPush/index.js +2 -3
- package/dist/mcp-server/tools/gitPush/logic.js +73 -181
- package/dist/mcp-server/tools/gitPush/registration.js +53 -75
- package/dist/mcp-server/tools/gitRebase/index.js +3 -5
- package/dist/mcp-server/tools/gitRebase/logic.js +73 -202
- package/dist/mcp-server/tools/gitRebase/registration.js +52 -70
- package/dist/mcp-server/tools/gitRemote/index.js +3 -5
- package/dist/mcp-server/tools/gitRemote/logic.js +85 -193
- package/dist/mcp-server/tools/gitRemote/registration.js +52 -65
- package/dist/mcp-server/tools/gitReset/index.js +2 -3
- package/dist/mcp-server/tools/gitReset/logic.js +37 -121
- package/dist/mcp-server/tools/gitReset/registration.js +53 -60
- package/dist/mcp-server/tools/gitSetWorkingDir/index.js +3 -5
- package/dist/mcp-server/tools/gitSetWorkingDir/logic.js +45 -133
- package/dist/mcp-server/tools/gitSetWorkingDir/registration.js +55 -85
- package/dist/mcp-server/tools/gitShow/index.js +3 -5
- package/dist/mcp-server/tools/gitShow/logic.js +33 -122
- package/dist/mcp-server/tools/gitShow/registration.js +52 -74
- package/dist/mcp-server/tools/gitStash/index.js +3 -5
- package/dist/mcp-server/tools/gitStash/logic.js +70 -214
- package/dist/mcp-server/tools/gitStash/registration.js +52 -77
- package/dist/mcp-server/tools/gitStatus/index.js +2 -4
- package/dist/mcp-server/tools/gitStatus/logic.js +82 -229
- package/dist/mcp-server/tools/gitStatus/registration.js +52 -66
- package/dist/mcp-server/tools/gitTag/index.js +3 -5
- package/dist/mcp-server/tools/gitTag/logic.js +66 -188
- package/dist/mcp-server/tools/gitTag/registration.js +54 -73
- package/dist/mcp-server/tools/gitWorktree/index.js +3 -5
- package/dist/mcp-server/tools/gitWorktree/logic.js +112 -322
- package/dist/mcp-server/tools/gitWorktree/registration.js +54 -58
- package/dist/mcp-server/tools/gitWrapupInstructions/index.js +5 -3
- package/dist/mcp-server/tools/gitWrapupInstructions/logic.js +26 -38
- package/dist/mcp-server/tools/gitWrapupInstructions/registration.js +54 -70
- package/dist/mcp-server/transports/httpTransport.js +2 -3
- package/package.json +12 -12
|
@@ -1,78 +1,64 @@
|
|
|
1
|
-
// Import utils from barrel (logger from ../utils/internal/logger.js)
|
|
2
|
-
import { logger } from "../../../utils/index.js";
|
|
3
|
-
// Import utils from barrel (ErrorHandler from ../utils/internal/errorHandler.js)
|
|
4
|
-
import { ErrorHandler } from "../../../utils/index.js";
|
|
5
|
-
// Import utils from barrel (requestContextService from ../utils/internal/requestContext.js)
|
|
6
|
-
import { BaseErrorCode } from "../../../types-global/errors.js"; // Keep direct import for types-global
|
|
7
|
-
import { requestContextService } from "../../../utils/index.js";
|
|
8
|
-
import { GitBranchBaseSchema, gitBranchLogic, } from "./logic.js";
|
|
9
|
-
let _getWorkingDirectory;
|
|
10
|
-
let _getSessionId;
|
|
11
1
|
/**
|
|
12
|
-
*
|
|
13
|
-
* @
|
|
14
|
-
* @param getSidFn - Function to get the session ID from context.
|
|
2
|
+
* @fileoverview Handles registration and error handling for the git_branch tool.
|
|
3
|
+
* @module src/mcp-server/tools/gitBranch/registration
|
|
15
4
|
*/
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
logger.info("State accessors initialized for git_branch tool registration.");
|
|
20
|
-
}
|
|
5
|
+
import { BaseErrorCode, McpError } from "../../../types-global/errors.js";
|
|
6
|
+
import { ErrorHandler, logger, requestContextService } from "../../../utils/index.js";
|
|
7
|
+
import { GitBranchBaseSchema, gitBranchLogic, GitBranchOutputSchema } from "./logic.js";
|
|
21
8
|
const TOOL_NAME = "git_branch";
|
|
22
9
|
const TOOL_DESCRIPTION = "Manages Git branches. Supports listing (local, remote, all), creating, deleting (with force), renaming (with force), and showing the current branch. Returns results as a JSON object.";
|
|
23
10
|
/**
|
|
24
|
-
* Registers the git_branch tool with the MCP server.
|
|
25
|
-
*
|
|
26
|
-
* @param
|
|
27
|
-
* @
|
|
28
|
-
* @throws {Error} If registration fails or state accessors are not initialized.
|
|
11
|
+
* Registers the git_branch 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 const registerGitBranchTool = async (server) => {
|
|
31
|
-
if (!_getWorkingDirectory || !_getSessionId) {
|
|
32
|
-
throw new Error("State accessors for git_branch must be initialized before registration.");
|
|
33
|
-
}
|
|
16
|
+
export const registerGitBranchTool = async (server, getWorkingDirectory, getSessionId) => {
|
|
34
17
|
const operation = "registerGitBranchTool";
|
|
35
18
|
const context = requestContextService.createRequestContext({ operation });
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
19
|
+
server.registerTool(TOOL_NAME, {
|
|
20
|
+
title: "Git Branch",
|
|
21
|
+
description: TOOL_DESCRIPTION,
|
|
22
|
+
inputSchema: GitBranchBaseSchema.shape,
|
|
23
|
+
outputSchema: GitBranchOutputSchema.shape,
|
|
24
|
+
annotations: {
|
|
25
|
+
readOnlyHint: false, // Can be destructive (delete/rename)
|
|
26
|
+
destructiveHint: true,
|
|
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 gitBranchLogic(params, {
|
|
38
|
+
...handlerContext,
|
|
39
|
+
getWorkingDirectory: () => getWorkingDirectory(sessionId),
|
|
46
40
|
});
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
...requestContext,
|
|
51
|
-
sessionId: sessionId,
|
|
52
|
-
getWorkingDirectory: getWorkingDirectoryForSession,
|
|
41
|
+
return {
|
|
42
|
+
structuredContent: result,
|
|
43
|
+
content: [{ type: "text", text: `Success: ${JSON.stringify(result, null, 2)}` }],
|
|
53
44
|
};
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
};
|
|
62
|
-
if (branchResult.success) {
|
|
63
|
-
logger.info(`Tool ${TOOL_NAME} (mode: ${toolInput.mode}) executed successfully, returning JSON`, logicContext);
|
|
64
|
-
}
|
|
65
|
-
else {
|
|
66
|
-
logger.warning(`Tool ${TOOL_NAME} (mode: ${toolInput.mode}) failed: ${branchResult.message}`, { ...logicContext, errorDetails: branchResult.error });
|
|
67
|
-
}
|
|
68
|
-
return { content: [resultContent] };
|
|
69
|
-
}, {
|
|
70
|
-
operation: toolOperation,
|
|
71
|
-
context: logicContext,
|
|
72
|
-
input: validatedArgs,
|
|
73
|
-
errorCode: BaseErrorCode.INTERNAL_ERROR,
|
|
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,
|
|
74
52
|
});
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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);
|
|
78
64
|
};
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @fileoverview Barrel file for the gitCheckout tool.
|
|
3
|
+
* @module src/mcp-server/tools/gitCheckout/index
|
|
3
4
|
*/
|
|
4
|
-
export { registerGitCheckoutTool
|
|
5
|
-
// Export types if needed elsewhere, e.g.:
|
|
6
|
-
// export type { GitCheckoutInput, GitCheckoutResult } from './logic.js';
|
|
5
|
+
export { registerGitCheckoutTool } from "./registration.js";
|
|
@@ -1,158 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Defines the core logic, schemas, and types for the git_checkout tool.
|
|
3
|
+
* @module src/mcp-server/tools/gitCheckout/logic
|
|
4
|
+
*/
|
|
1
5
|
import { execFile } from "child_process";
|
|
2
6
|
import { promisify } from "util";
|
|
3
7
|
import { z } from "zod";
|
|
4
|
-
|
|
5
|
-
import {
|
|
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
|
-
//
|
|
11
|
+
// 1. DEFINE the Zod input schema.
|
|
12
12
|
export const GitCheckoutInputSchema = z.object({
|
|
13
|
-
path: z
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
.string()
|
|
25
|
-
.optional()
|
|
26
|
-
.describe("Create a new branch named <new_branch> (e.g., 'feat/new-feature') and start it at <branchOrPath>."),
|
|
27
|
-
force: z
|
|
28
|
-
.boolean()
|
|
29
|
-
.optional()
|
|
30
|
-
.default(false)
|
|
31
|
-
.describe("Force checkout even if there are uncommitted changes (use with caution, discards local changes)."),
|
|
32
|
-
// Add other relevant git checkout options as needed (e.g., --track, -b for new branch shorthand)
|
|
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
|
+
branchOrPath: z.string().min(1).describe("The branch, commit, tag, or file path to checkout."),
|
|
15
|
+
newBranch: z.string().optional().describe("Create a new branch with this name before checking out."),
|
|
16
|
+
force: z.boolean().default(false).describe("Force checkout, discarding local changes."),
|
|
17
|
+
});
|
|
18
|
+
// 2. DEFINE the Zod response schema.
|
|
19
|
+
export const GitCheckoutOutputSchema = 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
|
+
currentBranch: z.string().optional().describe("The name of the current branch after the operation."),
|
|
23
|
+
newBranchCreated: z.boolean().optional().describe("Indicates if a new branch was created."),
|
|
33
24
|
});
|
|
34
25
|
/**
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* @param {GitCheckoutInput} input - The validated input object.
|
|
39
|
-
* @param {RequestContext} context - The request context for logging and error handling.
|
|
40
|
-
* @returns {Promise<GitCheckoutResult>} A promise that resolves with the structured checkout result.
|
|
41
|
-
* @throws {McpError} Throws an McpError if path resolution, validation, or the git command fails unexpectedly.
|
|
26
|
+
* 4. IMPLEMENT the core logic function.
|
|
27
|
+
* @throws {McpError} If the logic encounters an unrecoverable issue.
|
|
42
28
|
*/
|
|
43
|
-
export async function checkoutGit(
|
|
29
|
+
export async function checkoutGit(params, context) {
|
|
44
30
|
const operation = "checkoutGit";
|
|
45
|
-
logger.debug(`Executing ${operation}`, { ...context,
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
if (input.path && input.path !== ".") {
|
|
50
|
-
targetPath = input.path;
|
|
51
|
-
}
|
|
52
|
-
else {
|
|
53
|
-
const workingDir = context.getWorkingDirectory();
|
|
54
|
-
if (!workingDir) {
|
|
55
|
-
throw new McpError(BaseErrorCode.VALIDATION_ERROR, "No path provided and no working directory set for the session.", { context, operation });
|
|
56
|
-
}
|
|
57
|
-
targetPath = workingDir;
|
|
58
|
-
}
|
|
59
|
-
targetPath = sanitization.sanitizePath(targetPath, {
|
|
60
|
-
allowAbsolute: true,
|
|
61
|
-
}).sanitizedPath;
|
|
62
|
-
logger.debug("Sanitized path", {
|
|
63
|
-
...context,
|
|
64
|
-
operation,
|
|
65
|
-
sanitizedPath: targetPath,
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
catch (error) {
|
|
69
|
-
logger.error("Path resolution or sanitization failed", {
|
|
70
|
-
...context,
|
|
71
|
-
operation,
|
|
72
|
-
error,
|
|
73
|
-
});
|
|
74
|
-
if (error instanceof McpError)
|
|
75
|
-
throw error;
|
|
76
|
-
throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Invalid path: ${error instanceof Error ? error.message : String(error)}`, { context, operation, originalError: error });
|
|
31
|
+
logger.debug(`Executing ${operation}`, { ...context, params });
|
|
32
|
+
const workingDir = context.getWorkingDirectory();
|
|
33
|
+
if (params.path === "." && !workingDir) {
|
|
34
|
+
throw new McpError(BaseErrorCode.VALIDATION_ERROR, "No session working directory set. Please specify a 'path' or use 'git_set_working_dir' first.");
|
|
77
35
|
}
|
|
36
|
+
const targetPath = sanitization.sanitizePath(params.path === "." ? workingDir : params.path, { allowAbsolute: true }).sanitizedPath;
|
|
37
|
+
const args = ["-C", targetPath, "checkout"];
|
|
38
|
+
if (params.force)
|
|
39
|
+
args.push("--force");
|
|
40
|
+
if (params.newBranch)
|
|
41
|
+
args.push("-b", params.newBranch);
|
|
42
|
+
args.push(params.branchOrPath);
|
|
78
43
|
try {
|
|
79
|
-
|
|
80
|
-
const args = ["-C", targetPath, "checkout"];
|
|
81
|
-
if (input.force) {
|
|
82
|
-
args.push("--force");
|
|
83
|
-
}
|
|
84
|
-
if (input.newBranch) {
|
|
85
|
-
args.push("-b", input.newBranch);
|
|
86
|
-
}
|
|
87
|
-
args.push(input.branchOrPath); // Add the target branch/path
|
|
88
|
-
logger.debug(`Executing command: git ${args.join(" ")}`, {
|
|
89
|
-
...context,
|
|
90
|
-
operation,
|
|
91
|
-
});
|
|
92
|
-
// Execute command. Checkout often uses stderr for status messages.
|
|
44
|
+
logger.debug(`Executing command: git ${args.join(" ")}`, { ...context, operation });
|
|
93
45
|
const { stdout, stderr } = await execFileAsync("git", args);
|
|
94
46
|
const message = stderr.trim() || stdout.trim();
|
|
95
|
-
logger.
|
|
96
|
-
if (stderr) {
|
|
97
|
-
logger.debug(`Git checkout stderr: ${stderr}`, { ...context, operation });
|
|
98
|
-
}
|
|
99
|
-
// Get the current branch name after the checkout operation
|
|
47
|
+
logger.info("git checkout executed successfully", { ...context, operation, message });
|
|
100
48
|
let currentBranch;
|
|
101
49
|
try {
|
|
102
|
-
const { stdout: branchStdout } = await execFileAsync("git", [
|
|
103
|
-
"-C",
|
|
104
|
-
targetPath,
|
|
105
|
-
"branch",
|
|
106
|
-
"--show-current",
|
|
107
|
-
]);
|
|
50
|
+
const { stdout: branchStdout } = await execFileAsync("git", ["-C", targetPath, "branch", "--show-current"]);
|
|
108
51
|
currentBranch = branchStdout.trim();
|
|
109
52
|
}
|
|
110
|
-
catch
|
|
111
|
-
// This can fail in detached HEAD state, which is not an error for checkout
|
|
53
|
+
catch {
|
|
112
54
|
currentBranch = "Detached HEAD";
|
|
113
55
|
}
|
|
114
|
-
|
|
56
|
+
return {
|
|
115
57
|
success: true,
|
|
116
58
|
message,
|
|
117
59
|
currentBranch,
|
|
118
|
-
newBranchCreated: !!
|
|
60
|
+
newBranchCreated: !!params.newBranch,
|
|
119
61
|
};
|
|
120
|
-
logger.info("git checkout executed successfully", {
|
|
121
|
-
...context,
|
|
122
|
-
operation,
|
|
123
|
-
path: targetPath,
|
|
124
|
-
result,
|
|
125
|
-
});
|
|
126
|
-
return result;
|
|
127
62
|
}
|
|
128
63
|
catch (error) {
|
|
129
|
-
logger.error(`Failed to execute git checkout command`, {
|
|
130
|
-
...context,
|
|
131
|
-
operation,
|
|
132
|
-
path: targetPath,
|
|
133
|
-
error: error.message,
|
|
134
|
-
stderr: error.stderr,
|
|
135
|
-
stdout: error.stdout,
|
|
136
|
-
});
|
|
137
64
|
const errorMessage = error.stderr || error.stdout || error.message || "";
|
|
138
|
-
|
|
65
|
+
logger.error(`Failed to execute git checkout command`, { ...context, operation, errorMessage });
|
|
139
66
|
if (errorMessage.toLowerCase().includes("not a git repository")) {
|
|
140
|
-
throw new McpError(BaseErrorCode.NOT_FOUND, `Path is not a Git repository: ${targetPath}
|
|
67
|
+
throw new McpError(BaseErrorCode.NOT_FOUND, `Path is not a Git repository: ${targetPath}`);
|
|
141
68
|
}
|
|
142
|
-
if (errorMessage.match(/pathspec '.*?' did not match
|
|
143
|
-
throw new McpError(BaseErrorCode.NOT_FOUND, `Branch or pathspec not found: ${
|
|
69
|
+
if (errorMessage.match(/pathspec '.*?' did not match/)) {
|
|
70
|
+
throw new McpError(BaseErrorCode.NOT_FOUND, `Branch or pathspec not found: ${params.branchOrPath}.`);
|
|
144
71
|
}
|
|
145
72
|
if (errorMessage.includes("already exists")) {
|
|
146
|
-
|
|
147
|
-
throw new McpError(BaseErrorCode.CONFLICT, `Cannot create new branch '${input.newBranch}': it already exists. Error: ${errorMessage}`, { context, operation, originalError: error });
|
|
73
|
+
throw new McpError(BaseErrorCode.CONFLICT, `Cannot create new branch '${params.newBranch}': it already exists.`);
|
|
148
74
|
}
|
|
149
|
-
if (errorMessage.includes("
|
|
150
|
-
throw new McpError(BaseErrorCode.CONFLICT,
|
|
75
|
+
if (errorMessage.includes("overwritten by checkout")) {
|
|
76
|
+
throw new McpError(BaseErrorCode.CONFLICT, "Checkout failed due to uncommitted local changes. Stash or commit them, or use --force.");
|
|
151
77
|
}
|
|
152
78
|
if (errorMessage.includes("invalid reference")) {
|
|
153
|
-
throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Invalid branch name or reference: ${
|
|
79
|
+
throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Invalid branch name or reference: ${params.branchOrPath}.`);
|
|
154
80
|
}
|
|
155
|
-
|
|
156
|
-
throw new McpError(BaseErrorCode.INTERNAL_ERROR, `Failed to checkout for path: ${targetPath}. Error: ${errorMessage}`, { context, operation, originalError: error });
|
|
81
|
+
throw new McpError(BaseErrorCode.INTERNAL_ERROR, `Git checkout failed: ${errorMessage}`);
|
|
157
82
|
}
|
|
158
83
|
}
|
|
@@ -1,83 +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 { checkoutGit, GitCheckoutInputSchema, } from "./logic.js";
|
|
9
|
-
let _getWorkingDirectory;
|
|
10
|
-
let _getSessionId;
|
|
11
1
|
/**
|
|
12
|
-
*
|
|
13
|
-
*
|
|
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_checkout tool.
|
|
3
|
+
* @module src/mcp-server/tools/gitCheckout/registration
|
|
16
4
|
*/
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
logger.info("State accessors initialized for git_checkout tool registration.");
|
|
21
|
-
}
|
|
5
|
+
import { ErrorHandler, logger, requestContextService } from "../../../utils/index.js";
|
|
6
|
+
import { McpError, BaseErrorCode } from "../../../types-global/errors.js";
|
|
7
|
+
import { checkoutGit, GitCheckoutInputSchema, GitCheckoutOutputSchema, } from "./logic.js";
|
|
22
8
|
const TOOL_NAME = "git_checkout";
|
|
23
9
|
const TOOL_DESCRIPTION = "Switches branches or restores working tree files. Can checkout branches, commits, tags, or specific file paths. Supports creating new branches and forcing checkout.";
|
|
24
10
|
/**
|
|
25
|
-
* Registers the git_checkout tool with the MCP server.
|
|
26
|
-
*
|
|
27
|
-
* @param
|
|
28
|
-
* @
|
|
11
|
+
* Registers the git_checkout 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
|
|
31
|
-
if (!_getWorkingDirectory || !_getSessionId) {
|
|
32
|
-
throw new Error("State accessors for git_checkout must be initialized before registration.");
|
|
33
|
-
}
|
|
16
|
+
export const registerGitCheckoutTool = async (server, getWorkingDirectory, getSessionId) => {
|
|
34
17
|
const operation = "registerGitCheckoutTool";
|
|
35
18
|
const context = requestContextService.createRequestContext({ operation });
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
19
|
+
server.registerTool(TOOL_NAME, {
|
|
20
|
+
title: "Git Checkout",
|
|
21
|
+
description: TOOL_DESCRIPTION,
|
|
22
|
+
inputSchema: GitCheckoutInputSchema.shape,
|
|
23
|
+
outputSchema: GitCheckoutOutputSchema.shape,
|
|
24
|
+
annotations: {
|
|
25
|
+
readOnlyHint: false,
|
|
26
|
+
destructiveHint: true, // Can discard local changes with --force
|
|
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 checkoutGit(params, {
|
|
38
|
+
...handlerContext,
|
|
39
|
+
getWorkingDirectory: () => getWorkingDirectory(sessionId),
|
|
43
40
|
});
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
text: JSON.stringify(checkoutResult, null, 2), // Pretty-print JSON
|
|
61
|
-
contentType: "application/json",
|
|
62
|
-
};
|
|
63
|
-
// Log based on the success flag in the result
|
|
64
|
-
if (checkoutResult.success) {
|
|
65
|
-
logger.info(`Tool ${TOOL_NAME} executed successfully: ${checkoutResult.message}`, logicContext);
|
|
66
|
-
}
|
|
67
|
-
else {
|
|
68
|
-
// Log non-fatal conditions like conflicts differently if needed
|
|
69
|
-
logger.info(`Tool ${TOOL_NAME} completed with status: ${checkoutResult.message}`, logicContext);
|
|
70
|
-
}
|
|
71
|
-
// Even if success is false (e.g., due to conflicts reported by logic),
|
|
72
|
-
// it's not a tool execution error unless the logic threw an McpError.
|
|
73
|
-
return { content: [resultContent] };
|
|
74
|
-
}, {
|
|
75
|
-
operation: toolOperation,
|
|
76
|
-
context: logicContext,
|
|
77
|
-
input: validatedArgs,
|
|
78
|
-
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,
|
|
79
52
|
});
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
|
3
|
-
*
|
|
2
|
+
* @fileoverview Barrel file for the gitCherryPick tool.
|
|
3
|
+
* @module src/mcp-server/tools/gitCherryPick/index
|
|
4
4
|
*/
|
|
5
|
-
export { registerGitCherryPickTool
|
|
6
|
-
// Export types if needed elsewhere, e.g.:
|
|
7
|
-
// export type { GitCherryPickInput, GitCherryPickResult } from './logic.js';
|
|
5
|
+
export { registerGitCherryPickTool } from "./registration.js";
|