@cyanheads/git-mcp-server 2.1.8 → 2.2.1
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 +40 -116
- package/dist/mcp-server/tools/gitAdd/registration.js +39 -59
- package/dist/mcp-server/tools/gitBranch/index.js +3 -5
- package/dist/mcp-server/tools/gitBranch/logic.js +109 -304
- 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 -144
- 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 +47 -173
- 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 +45 -154
- 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 +18 -32
- 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 +47 -187
- 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 +75 -310
- 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 +72 -264
- 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 +38 -136
- 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 +40 -162
- 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 +71 -266
- 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 +45 -191
- 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 +39 -156
- 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 +65 -192
- 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 +59 -207
- 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 +76 -200
- 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 +33 -133
- 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 +39 -144
- 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 +28 -133
- 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 +59 -219
- 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 +79 -236
- 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 +57 -198
- 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 +102 -328
- 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 +25 -43
- package/dist/mcp-server/tools/gitWrapupInstructions/registration.js +54 -70
- package/dist/mcp-server/transports/httpTransport.js +2 -3
- 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
|
-
*
|
|
8
|
-
*
|
|
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
|
-
|
|
13
|
-
|
|
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 { resetGitState, GitResetInputSchema, GitResetOutputSchema, } from "./logic.js";
|
|
17
7
|
const TOOL_NAME = "git_reset";
|
|
18
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.";
|
|
19
9
|
/**
|
|
20
|
-
* Registers the git_reset tool with the MCP server.
|
|
21
|
-
*
|
|
22
|
-
* @param
|
|
23
|
-
* @
|
|
10
|
+
* Registers the git_reset tool with the MCP server instance.
|
|
11
|
+
* @param server The MCP server instance.
|
|
12
|
+
* @param getWorkingDirectory Function to get the session's working directory.
|
|
13
|
+
* @param getSessionId Function to get the session ID from context.
|
|
24
14
|
*/
|
|
25
|
-
export async
|
|
26
|
-
if (!_getWorkingDirectory || !_getSessionId) {
|
|
27
|
-
throw new Error("State accessors for git_reset must be initialized before registration.");
|
|
28
|
-
}
|
|
15
|
+
export const registerGitResetTool = async (server, getWorkingDirectory, getSessionId) => {
|
|
29
16
|
const operation = "registerGitResetTool";
|
|
30
17
|
const context = requestContextService.createRequestContext({ operation });
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
18
|
+
server.registerTool(TOOL_NAME, {
|
|
19
|
+
title: "Git Reset",
|
|
20
|
+
description: TOOL_DESCRIPTION,
|
|
21
|
+
inputSchema: GitResetInputSchema.shape,
|
|
22
|
+
outputSchema: GitResetOutputSchema.shape,
|
|
23
|
+
annotations: {
|
|
24
|
+
readOnlyHint: false,
|
|
25
|
+
destructiveHint: true, // Can be very destructive, especially in 'hard' mode
|
|
26
|
+
idempotentHint: false,
|
|
27
|
+
openWorldHint: false,
|
|
28
|
+
},
|
|
29
|
+
}, async (params, callContext) => {
|
|
30
|
+
const handlerContext = requestContextService.createRequestContext({
|
|
31
|
+
toolName: TOOL_NAME,
|
|
32
|
+
parentContext: callContext,
|
|
33
|
+
});
|
|
34
|
+
try {
|
|
35
|
+
const sessionId = getSessionId(handlerContext);
|
|
36
|
+
const result = await resetGitState(params, {
|
|
37
|
+
...handlerContext,
|
|
38
|
+
getWorkingDirectory: () => getWorkingDirectory(sessionId),
|
|
38
39
|
});
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
};
|
|
43
|
-
const logicContext = {
|
|
44
|
-
...requestContext,
|
|
45
|
-
sessionId: sessionId,
|
|
46
|
-
getWorkingDirectory: getWorkingDirectoryForSession,
|
|
40
|
+
return {
|
|
41
|
+
structuredContent: result,
|
|
42
|
+
content: [{ type: "text", text: `Success: ${JSON.stringify(result, null, 2)}` }],
|
|
47
43
|
};
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
logger.error(`Error in ${TOOL_NAME} handler`, { error, ...handlerContext });
|
|
47
|
+
const mcpError = ErrorHandler.handleError(error, {
|
|
48
|
+
operation: `tool:${TOOL_NAME}`,
|
|
49
|
+
context: handlerContext,
|
|
50
|
+
input: params,
|
|
67
51
|
});
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
52
|
+
return {
|
|
53
|
+
isError: true,
|
|
54
|
+
content: [{ type: "text", text: `Error: ${mcpError.message}` }],
|
|
55
|
+
structuredContent: {
|
|
56
|
+
code: mcpError.code,
|
|
57
|
+
message: mcpError.message,
|
|
58
|
+
details: mcpError.details,
|
|
59
|
+
},
|
|
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 gitSetWorkingDir tool.
|
|
3
|
+
* @module src/mcp-server/tools/gitSetWorkingDir/index
|
|
4
4
|
*/
|
|
5
|
-
export { registerGitSetWorkingDirTool
|
|
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,54 @@
|
|
|
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 {
|
|
6
|
-
import {
|
|
9
|
+
import { logger, sanitization } from "../../../utils/index.js";
|
|
10
|
+
import { McpError, BaseErrorCode } from "../../../types-global/errors.js";
|
|
7
11
|
const execFileAsync = promisify(execFile);
|
|
8
|
-
//
|
|
12
|
+
// 1. DEFINE the Zod input schema.
|
|
9
13
|
export const GitSetWorkingDirInputSchema = z.object({
|
|
10
|
-
path: z
|
|
11
|
-
|
|
12
|
-
|
|
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
|
});
|
|
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) {
|
|
26
|
+
const { stdout } = await execFileAsync("git", ["rev-parse", "--is-inside-work-tree"], { cwd: path }).catch(() => ({ stdout: "false" }));
|
|
27
|
+
return stdout.trim() === "true";
|
|
28
|
+
}
|
|
24
29
|
/**
|
|
25
|
-
*
|
|
26
|
-
*
|
|
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.
|
|
30
|
+
* 4. IMPLEMENT the core logic function.
|
|
31
|
+
* @throws {McpError} If the logic encounters an unrecoverable issue.
|
|
33
32
|
*/
|
|
34
|
-
export async function gitSetWorkingDirLogic(
|
|
33
|
+
export async function gitSetWorkingDirLogic(params, context) {
|
|
35
34
|
const operation = "gitSetWorkingDirLogic";
|
|
36
|
-
logger.debug(`Executing ${operation}`, { ...context,
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
sanitizedPath = sanitization.sanitizePath(input.path, {
|
|
42
|
-
allowAbsolute: true,
|
|
43
|
-
}).sanitizedPath;
|
|
44
|
-
logger.debug(`Sanitized path: ${sanitizedPath}`, { ...context, operation });
|
|
35
|
+
logger.debug(`Executing ${operation}`, { ...context, params });
|
|
36
|
+
const sanitizedPath = sanitization.sanitizePath(params.path, { allowAbsolute: true }).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}`);
|
|
45
40
|
}
|
|
46
|
-
|
|
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 });
|
|
51
|
-
}
|
|
52
|
-
// Check if the directory exists
|
|
53
|
-
try {
|
|
54
|
-
const stats = await fs.stat(sanitizedPath);
|
|
55
|
-
if (!stats.isDirectory()) {
|
|
56
|
-
throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Path is not a directory: ${sanitizedPath}`, { context, operation });
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
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 });
|
|
69
|
-
}
|
|
70
|
-
let isGitRepo = false;
|
|
41
|
+
let isGitRepo = await checkIsGitRepo(sanitizedPath);
|
|
71
42
|
let initializedRepo = false;
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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 });
|
|
96
|
-
try {
|
|
97
|
-
await execFileAsync("git", ["init", "--initial-branch=main"], {
|
|
98
|
-
cwd: sanitizedPath,
|
|
99
|
-
});
|
|
100
|
-
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 });
|
|
103
|
-
}
|
|
104
|
-
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 });
|
|
111
|
-
}
|
|
112
|
-
}
|
|
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).";
|
|
43
|
+
if (!isGitRepo && params.initializeIfNotPresent) {
|
|
44
|
+
await execFileAsync("git", ["init", "--initial-branch=main"], { cwd: sanitizedPath });
|
|
45
|
+
initializedRepo = true;
|
|
46
|
+
isGitRepo = true;
|
|
145
47
|
}
|
|
146
|
-
|
|
147
|
-
|
|
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).";
|
|
48
|
+
if (params.validateGitRepo && !isGitRepo) {
|
|
49
|
+
throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Path is not a valid Git repository: ${sanitizedPath}.`);
|
|
152
50
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
path: sanitizedPath,
|
|
157
|
-
initialized: initializedRepo,
|
|
158
|
-
};
|
|
51
|
+
context.setWorkingDirectory(sanitizedPath);
|
|
52
|
+
const message = `Working directory set to: ${sanitizedPath}${initializedRepo ? " (New repository initialized)." : ""}`;
|
|
53
|
+
return { success: true, message, path: sanitizedPath, initialized: initializedRepo };
|
|
159
54
|
}
|
|
@@ -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
|
-
*
|
|
9
|
-
*
|
|
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
|
-
|
|
15
|
-
|
|
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 { gitSetWorkingDirLogic, GitSetWorkingDirInputSchema, GitSetWorkingDirOutputSchema, } from "./logic.js";
|
|
21
7
|
const TOOL_NAME = "git_set_working_dir";
|
|
22
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.";
|
|
23
9
|
/**
|
|
24
|
-
* Registers the git_set_working_dir tool with the MCP server.
|
|
25
|
-
*
|
|
26
|
-
* @param
|
|
27
|
-
* @
|
|
10
|
+
* Registers the git_set_working_dir tool with the MCP server instance.
|
|
11
|
+
* @param server The MCP server instance.
|
|
12
|
+
* @param setWorkingDirectory Function to set the session's working directory.
|
|
13
|
+
* @param getSessionId Function to get the session ID from context.
|
|
28
14
|
*/
|
|
29
|
-
export async
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
15
|
+
export const registerGitSetWorkingDirTool = async (server, setWorkingDirectory, getSessionId) => {
|
|
16
|
+
const operation = "registerGitSetWorkingDirTool";
|
|
17
|
+
const context = requestContextService.createRequestContext({ operation });
|
|
18
|
+
server.registerTool(TOOL_NAME, {
|
|
19
|
+
title: "Git Set Working Directory",
|
|
20
|
+
description: TOOL_DESCRIPTION,
|
|
21
|
+
inputSchema: GitSetWorkingDirInputSchema.shape,
|
|
22
|
+
outputSchema: GitSetWorkingDirOutputSchema.shape,
|
|
23
|
+
annotations: {
|
|
24
|
+
readOnlyHint: true, // Modifies session state, but not external files
|
|
25
|
+
destructiveHint: false,
|
|
26
|
+
idempotentHint: true,
|
|
27
|
+
openWorldHint: false,
|
|
28
|
+
},
|
|
29
|
+
}, async (params, callContext) => {
|
|
30
|
+
const handlerContext = requestContextService.createRequestContext({
|
|
31
|
+
toolName: TOOL_NAME,
|
|
32
|
+
parentContext: callContext,
|
|
33
|
+
});
|
|
34
|
+
try {
|
|
35
|
+
const sessionId = getSessionId(handlerContext);
|
|
36
|
+
const result = await gitSetWorkingDirLogic(params, {
|
|
37
|
+
...handlerContext,
|
|
38
|
+
setWorkingDirectory: (path) => setWorkingDirectory(sessionId, path),
|
|
43
39
|
});
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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
|
|
40
|
+
return {
|
|
41
|
+
structuredContent: result,
|
|
42
|
+
content: [{ type: "text", text: `Success: ${JSON.stringify(result, null, 2)}` }],
|
|
61
43
|
};
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
logger.error(`Error in ${TOOL_NAME} handler`, { error, ...handlerContext });
|
|
47
|
+
const mcpError = ErrorHandler.handleError(error, {
|
|
48
|
+
operation: `tool:${TOOL_NAME}`,
|
|
49
|
+
context: handlerContext,
|
|
50
|
+
input: params,
|
|
82
51
|
});
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}
|
|
94
|
-
}
|
|
52
|
+
return {
|
|
53
|
+
isError: true,
|
|
54
|
+
content: [{ type: "text", text: `Error: ${mcpError.message}` }],
|
|
55
|
+
structuredContent: {
|
|
56
|
+
code: mcpError.code,
|
|
57
|
+
message: mcpError.message,
|
|
58
|
+
details: mcpError.details,
|
|
59
|
+
},
|
|
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 gitShow tool.
|
|
3
|
+
* @module src/mcp-server/tools/gitShow/index
|
|
4
4
|
*/
|
|
5
|
-
export { registerGitShowTool
|
|
6
|
-
// Export types if needed elsewhere, e.g.:
|
|
7
|
-
// export type { GitShowInput, GitShowResult } from './logic.js';
|
|
5
|
+
export { registerGitShowTool } from "./registration.js";
|