@cyanheads/git-mcp-server 2.1.1 → 2.1.3
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 +91 -76
- package/dist/config/index.js +7 -7
- package/dist/index.js +35 -21
- package/dist/mcp-server/server.js +72 -56
- package/dist/mcp-server/tools/gitAdd/index.js +1 -1
- package/dist/mcp-server/tools/gitAdd/logic.js +88 -39
- package/dist/mcp-server/tools/gitAdd/registration.js +17 -14
- package/dist/mcp-server/tools/gitBranch/index.js +1 -1
- package/dist/mcp-server/tools/gitBranch/logic.js +213 -85
- package/dist/mcp-server/tools/gitBranch/registration.js +16 -13
- package/dist/mcp-server/tools/gitCheckout/index.js +1 -1
- package/dist/mcp-server/tools/gitCheckout/logic.js +85 -145
- package/dist/mcp-server/tools/gitCheckout/registration.js +16 -14
- package/dist/mcp-server/tools/gitCherryPick/index.js +1 -1
- package/dist/mcp-server/tools/gitCherryPick/logic.js +100 -41
- package/dist/mcp-server/tools/gitCherryPick/registration.js +21 -14
- package/dist/mcp-server/tools/gitClean/index.js +1 -1
- package/dist/mcp-server/tools/gitClean/logic.js +93 -41
- package/dist/mcp-server/tools/gitClean/registration.js +19 -16
- package/dist/mcp-server/tools/gitClearWorkingDir/index.js +1 -1
- package/dist/mcp-server/tools/gitClearWorkingDir/logic.js +14 -11
- package/dist/mcp-server/tools/gitClearWorkingDir/registration.js +19 -13
- package/dist/mcp-server/tools/gitClone/index.js +1 -1
- package/dist/mcp-server/tools/gitClone/logic.js +89 -30
- package/dist/mcp-server/tools/gitClone/registration.js +15 -12
- package/dist/mcp-server/tools/gitCommit/index.js +1 -1
- package/dist/mcp-server/tools/gitCommit/logic.js +198 -76
- package/dist/mcp-server/tools/gitCommit/registration.js +23 -20
- package/dist/mcp-server/tools/gitDiff/index.js +1 -1
- package/dist/mcp-server/tools/gitDiff/logic.js +124 -44
- package/dist/mcp-server/tools/gitDiff/registration.js +16 -14
- package/dist/mcp-server/tools/gitFetch/index.js +1 -1
- package/dist/mcp-server/tools/gitFetch/logic.js +78 -49
- package/dist/mcp-server/tools/gitFetch/registration.js +16 -14
- package/dist/mcp-server/tools/gitInit/index.js +1 -1
- package/dist/mcp-server/tools/gitInit/logic.js +88 -34
- package/dist/mcp-server/tools/gitInit/registration.js +32 -18
- package/dist/mcp-server/tools/gitLog/index.js +1 -1
- package/dist/mcp-server/tools/gitLog/logic.js +133 -47
- package/dist/mcp-server/tools/gitLog/registration.js +16 -14
- package/dist/mcp-server/tools/gitMerge/index.js +1 -1
- package/dist/mcp-server/tools/gitMerge/logic.js +102 -61
- package/dist/mcp-server/tools/gitMerge/registration.js +17 -14
- package/dist/mcp-server/tools/gitPull/index.js +1 -1
- package/dist/mcp-server/tools/gitPull/logic.js +90 -69
- package/dist/mcp-server/tools/gitPull/registration.js +16 -14
- package/dist/mcp-server/tools/gitPush/index.js +1 -1
- package/dist/mcp-server/tools/gitPush/logic.js +116 -100
- package/dist/mcp-server/tools/gitPush/registration.js +16 -14
- package/dist/mcp-server/tools/gitRebase/index.js +1 -1
- package/dist/mcp-server/tools/gitRebase/logic.js +121 -82
- package/dist/mcp-server/tools/gitRebase/registration.js +21 -14
- package/dist/mcp-server/tools/gitRemote/index.js +1 -1
- package/dist/mcp-server/tools/gitRemote/logic.js +108 -52
- package/dist/mcp-server/tools/gitRemote/registration.js +14 -11
- package/dist/mcp-server/tools/gitReset/index.js +1 -1
- package/dist/mcp-server/tools/gitReset/logic.js +65 -37
- package/dist/mcp-server/tools/gitReset/registration.js +14 -12
- package/dist/mcp-server/tools/gitSetWorkingDir/index.js +1 -1
- package/dist/mcp-server/tools/gitSetWorkingDir/logic.js +74 -34
- package/dist/mcp-server/tools/gitSetWorkingDir/registration.js +18 -11
- package/dist/mcp-server/tools/gitShow/index.js +1 -1
- package/dist/mcp-server/tools/gitShow/logic.js +78 -35
- package/dist/mcp-server/tools/gitShow/registration.js +17 -12
- package/dist/mcp-server/tools/gitStash/index.js +1 -1
- package/dist/mcp-server/tools/gitStash/logic.js +143 -58
- package/dist/mcp-server/tools/gitStash/registration.js +19 -12
- package/dist/mcp-server/tools/gitStatus/index.js +1 -1
- package/dist/mcp-server/tools/gitStatus/logic.js +100 -58
- package/dist/mcp-server/tools/gitStatus/registration.js +15 -12
- package/dist/mcp-server/tools/gitTag/index.js +1 -1
- package/dist/mcp-server/tools/gitTag/logic.js +124 -51
- package/dist/mcp-server/tools/gitTag/registration.js +14 -11
- package/dist/mcp-server/tools/gitWorktree/index.js +1 -1
- package/dist/mcp-server/tools/gitWorktree/logic.js +204 -95
- package/dist/mcp-server/tools/gitWorktree/registration.js +14 -11
- package/dist/mcp-server/tools/gitWrapupInstructions/index.js +1 -1
- package/dist/mcp-server/tools/gitWrapupInstructions/logic.js +21 -11
- package/dist/mcp-server/tools/gitWrapupInstructions/registration.js +14 -12
- package/dist/mcp-server/transports/httpTransport.js +187 -79
- package/dist/mcp-server/transports/stdioTransport.js +14 -8
- package/dist/types-global/errors.js +9 -4
- package/dist/utils/index.js +4 -4
- package/dist/utils/internal/errorHandler.js +62 -40
- package/dist/utils/internal/index.js +3 -3
- package/dist/utils/internal/logger.js +97 -54
- package/dist/utils/internal/requestContext.js +7 -5
- package/dist/utils/metrics/index.js +1 -1
- package/dist/utils/metrics/tokenCounter.js +18 -14
- package/dist/utils/parsing/dateParser.js +5 -5
- package/dist/utils/parsing/index.js +2 -2
- package/dist/utils/parsing/jsonParser.js +20 -11
- package/dist/utils/security/idGenerator.js +8 -10
- package/dist/utils/security/index.js +3 -3
- package/dist/utils/security/rateLimiter.js +16 -14
- package/dist/utils/security/sanitization.js +139 -82
- package/package.json +47 -25
|
@@ -12,40 +12,40 @@
|
|
|
12
12
|
* - Overview (Capabilities): https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-03-26/basic/index.mdx
|
|
13
13
|
* - Transports: https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-03-26/basic/transports.mdx
|
|
14
14
|
*/
|
|
15
|
-
import { McpServer } from
|
|
15
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
16
16
|
// Import validated configuration and environment details.
|
|
17
|
-
import { config, environment } from
|
|
17
|
+
import { config, environment } from "../config/index.js";
|
|
18
18
|
// Import core utilities: ErrorHandler, logger, requestContextService.
|
|
19
|
-
import { ErrorHandler, logger, requestContextService } from
|
|
19
|
+
import { ErrorHandler, logger, requestContextService, } from "../utils/index.js"; // Added RequestContext
|
|
20
20
|
// Import registration AND state initialization functions for ALL Git tools (alphabetized)
|
|
21
|
-
import { initializeGitAddStateAccessors, registerGitAddTool } from
|
|
22
|
-
import { initializeGitBranchStateAccessors, registerGitBranchTool } from
|
|
23
|
-
import { initializeGitCheckoutStateAccessors, registerGitCheckoutTool } from
|
|
24
|
-
import { initializeGitCherryPickStateAccessors, registerGitCherryPickTool } from
|
|
25
|
-
import { initializeGitCleanStateAccessors, registerGitCleanTool } from
|
|
26
|
-
import { initializeGitClearWorkingDirStateAccessors, registerGitClearWorkingDirTool } from
|
|
27
|
-
import { registerGitCloneTool } from
|
|
28
|
-
import { initializeGitCommitStateAccessors, registerGitCommitTool } from
|
|
29
|
-
import { initializeGitDiffStateAccessors, registerGitDiffTool } from
|
|
30
|
-
import { initializeGitFetchStateAccessors, registerGitFetchTool } from
|
|
31
|
-
import { initializeGitInitStateAccessors, registerGitInitTool } from
|
|
32
|
-
import { initializeGitLogStateAccessors, registerGitLogTool } from
|
|
33
|
-
import { initializeGitMergeStateAccessors, registerGitMergeTool } from
|
|
34
|
-
import { initializeGitPullStateAccessors, registerGitPullTool } from
|
|
35
|
-
import { initializeGitPushStateAccessors, registerGitPushTool } from
|
|
36
|
-
import { initializeGitRebaseStateAccessors, registerGitRebaseTool } from
|
|
37
|
-
import { initializeGitRemoteStateAccessors, registerGitRemoteTool } from
|
|
38
|
-
import { initializeGitResetStateAccessors, registerGitResetTool } from
|
|
39
|
-
import { initializeGitSetWorkingDirStateAccessors, registerGitSetWorkingDirTool } from
|
|
40
|
-
import { initializeGitShowStateAccessors, registerGitShowTool } from
|
|
41
|
-
import { initializeGitStashStateAccessors, registerGitStashTool } from
|
|
42
|
-
import { initializeGitStatusStateAccessors, registerGitStatusTool } from
|
|
43
|
-
import { initializeGitTagStateAccessors, registerGitTagTool } from
|
|
44
|
-
import { initializeGitWorktreeStateAccessors, registerGitWorktreeTool } from
|
|
45
|
-
import { initializeGitWrapupInstructionsStateAccessors, registerGitWrapupInstructionsTool } from
|
|
21
|
+
import { initializeGitAddStateAccessors, registerGitAddTool, } from "./tools/gitAdd/index.js";
|
|
22
|
+
import { initializeGitBranchStateAccessors, registerGitBranchTool, } from "./tools/gitBranch/index.js";
|
|
23
|
+
import { initializeGitCheckoutStateAccessors, registerGitCheckoutTool, } from "./tools/gitCheckout/index.js";
|
|
24
|
+
import { initializeGitCherryPickStateAccessors, registerGitCherryPickTool, } from "./tools/gitCherryPick/index.js";
|
|
25
|
+
import { initializeGitCleanStateAccessors, registerGitCleanTool, } from "./tools/gitClean/index.js";
|
|
26
|
+
import { initializeGitClearWorkingDirStateAccessors, registerGitClearWorkingDirTool, } from "./tools/gitClearWorkingDir/index.js";
|
|
27
|
+
import { registerGitCloneTool } from "./tools/gitClone/index.js"; // No initializer needed/available
|
|
28
|
+
import { initializeGitCommitStateAccessors, registerGitCommitTool, } from "./tools/gitCommit/index.js";
|
|
29
|
+
import { initializeGitDiffStateAccessors, registerGitDiffTool, } from "./tools/gitDiff/index.js";
|
|
30
|
+
import { initializeGitFetchStateAccessors, registerGitFetchTool, } from "./tools/gitFetch/index.js";
|
|
31
|
+
import { initializeGitInitStateAccessors, registerGitInitTool, } from "./tools/gitInit/index.js";
|
|
32
|
+
import { initializeGitLogStateAccessors, registerGitLogTool, } from "./tools/gitLog/index.js";
|
|
33
|
+
import { initializeGitMergeStateAccessors, registerGitMergeTool, } from "./tools/gitMerge/index.js";
|
|
34
|
+
import { initializeGitPullStateAccessors, registerGitPullTool, } from "./tools/gitPull/index.js";
|
|
35
|
+
import { initializeGitPushStateAccessors, registerGitPushTool, } from "./tools/gitPush/index.js";
|
|
36
|
+
import { initializeGitRebaseStateAccessors, registerGitRebaseTool, } from "./tools/gitRebase/index.js";
|
|
37
|
+
import { initializeGitRemoteStateAccessors, registerGitRemoteTool, } from "./tools/gitRemote/index.js";
|
|
38
|
+
import { initializeGitResetStateAccessors, registerGitResetTool, } from "./tools/gitReset/index.js";
|
|
39
|
+
import { initializeGitSetWorkingDirStateAccessors, registerGitSetWorkingDirTool, } from "./tools/gitSetWorkingDir/index.js";
|
|
40
|
+
import { initializeGitShowStateAccessors, registerGitShowTool, } from "./tools/gitShow/index.js";
|
|
41
|
+
import { initializeGitStashStateAccessors, registerGitStashTool, } from "./tools/gitStash/index.js";
|
|
42
|
+
import { initializeGitStatusStateAccessors, registerGitStatusTool, } from "./tools/gitStatus/index.js";
|
|
43
|
+
import { initializeGitTagStateAccessors, registerGitTagTool, } from "./tools/gitTag/index.js";
|
|
44
|
+
import { initializeGitWorktreeStateAccessors, registerGitWorktreeTool, } from "./tools/gitWorktree/index.js";
|
|
45
|
+
import { initializeGitWrapupInstructionsStateAccessors, registerGitWrapupInstructionsTool, } from "./tools/gitWrapupInstructions/index.js";
|
|
46
46
|
// Import transport setup functions AND state accessors
|
|
47
|
-
import { getHttpSessionWorkingDirectory, setHttpSessionWorkingDirectory, startHttpTransport } from
|
|
48
|
-
import { connectStdioTransport, getStdioWorkingDirectory, setStdioWorkingDirectory } from
|
|
47
|
+
import { getHttpSessionWorkingDirectory, setHttpSessionWorkingDirectory, startHttpTransport, } from "./transports/httpTransport.js";
|
|
48
|
+
import { connectStdioTransport, getStdioWorkingDirectory, setStdioWorkingDirectory, } from "./transports/stdioTransport.js";
|
|
49
49
|
/**
|
|
50
50
|
* Creates and configures a new instance of the McpServer.
|
|
51
51
|
*
|
|
@@ -79,8 +79,8 @@ import { connectStdioTransport, getStdioWorkingDirectory, setStdioWorkingDirecto
|
|
|
79
79
|
*/
|
|
80
80
|
// Removed sessionId parameter, it will be retrieved from context within tool handlers
|
|
81
81
|
async function createMcpServerInstance() {
|
|
82
|
-
const context = { operation:
|
|
83
|
-
logger.info(
|
|
82
|
+
const context = { operation: "createMcpServerInstance" };
|
|
83
|
+
logger.info("Initializing MCP server instance", context);
|
|
84
84
|
// Configure the request context service (used for correlating logs/errors).
|
|
85
85
|
requestContextService.configure({
|
|
86
86
|
appName: config.mcpServerName,
|
|
@@ -90,10 +90,26 @@ async function createMcpServerInstance() {
|
|
|
90
90
|
// Instantiate the core McpServer using the SDK.
|
|
91
91
|
// Provide server identity (name, version) and declare supported capabilities.
|
|
92
92
|
// Note: Resources capability declared, but none are registered currently.
|
|
93
|
-
logger.debug(
|
|
93
|
+
logger.debug("Instantiating McpServer with capabilities", {
|
|
94
|
+
...context,
|
|
95
|
+
serverInfo: {
|
|
96
|
+
name: config.mcpServerName,
|
|
97
|
+
version: config.mcpServerVersion,
|
|
98
|
+
},
|
|
99
|
+
capabilities: {
|
|
100
|
+
logging: {},
|
|
101
|
+
resources: { listChanged: true },
|
|
102
|
+
tools: { listChanged: true },
|
|
103
|
+
},
|
|
104
|
+
});
|
|
94
105
|
const server = new McpServer({ name: config.mcpServerName, version: config.mcpServerVersion }, // ServerInformation part of InitializeResult
|
|
95
|
-
{
|
|
96
|
-
|
|
106
|
+
{
|
|
107
|
+
capabilities: {
|
|
108
|
+
logging: {},
|
|
109
|
+
resources: { listChanged: true },
|
|
110
|
+
tools: { listChanged: true },
|
|
111
|
+
},
|
|
112
|
+
});
|
|
97
113
|
// --- Define Unified State Accessor Functions ---
|
|
98
114
|
// These functions abstract away the transport type to get/set session state.
|
|
99
115
|
/** Gets the session ID from the tool's execution context. */
|
|
@@ -103,9 +119,9 @@ async function createMcpServerInstance() {
|
|
|
103
119
|
};
|
|
104
120
|
/** Gets the working directory based on transport type and session ID. */
|
|
105
121
|
const getWorkingDirectory = (sessionId) => {
|
|
106
|
-
if (config.mcpTransportType ===
|
|
122
|
+
if (config.mcpTransportType === "http") {
|
|
107
123
|
if (!sessionId) {
|
|
108
|
-
logger.warning(
|
|
124
|
+
logger.warning("Attempted to get HTTP working directory without session ID", { ...context, caller: "getWorkingDirectory" });
|
|
109
125
|
return undefined;
|
|
110
126
|
}
|
|
111
127
|
return getHttpSessionWorkingDirectory(sessionId);
|
|
@@ -117,9 +133,9 @@ async function createMcpServerInstance() {
|
|
|
117
133
|
};
|
|
118
134
|
/** Sets the working directory based on transport type and session ID. */
|
|
119
135
|
const setWorkingDirectory = (sessionId, dir) => {
|
|
120
|
-
if (config.mcpTransportType ===
|
|
136
|
+
if (config.mcpTransportType === "http") {
|
|
121
137
|
if (!sessionId) {
|
|
122
|
-
logger.error(
|
|
138
|
+
logger.error("Attempted to set HTTP working directory without session ID", { ...context, caller: "setWorkingDirectory", dir });
|
|
123
139
|
// Optionally throw an error or just log
|
|
124
140
|
return;
|
|
125
141
|
}
|
|
@@ -132,7 +148,7 @@ async function createMcpServerInstance() {
|
|
|
132
148
|
};
|
|
133
149
|
// --- Initialize Tool State Accessors BEFORE Registration ---
|
|
134
150
|
// Pass the defined unified accessor functions to the initializers.
|
|
135
|
-
logger.debug(
|
|
151
|
+
logger.debug("Initializing state accessors for tools...", context);
|
|
136
152
|
try {
|
|
137
153
|
// Call initializers for all tools that likely need state access (alphabetized).
|
|
138
154
|
// If an initializer doesn't exist, the import would have failed earlier (or build will fail).
|
|
@@ -161,11 +177,11 @@ async function createMcpServerInstance() {
|
|
|
161
177
|
initializeGitTagStateAccessors(getWorkingDirectory, getSessionIdFromContext);
|
|
162
178
|
initializeGitWorktreeStateAccessors(getWorkingDirectory, getSessionIdFromContext);
|
|
163
179
|
initializeGitWrapupInstructionsStateAccessors(getWorkingDirectory, getSessionIdFromContext); // Added this line
|
|
164
|
-
logger.debug(
|
|
180
|
+
logger.debug("State accessors initialized successfully.", context);
|
|
165
181
|
}
|
|
166
182
|
catch (initError) {
|
|
167
183
|
// Catch errors specifically during initialization phase
|
|
168
|
-
logger.error(
|
|
184
|
+
logger.error("Failed during state accessor initialization", {
|
|
169
185
|
...context,
|
|
170
186
|
error: initError instanceof Error ? initError.message : String(initError),
|
|
171
187
|
stack: initError instanceof Error ? initError.stack : undefined,
|
|
@@ -175,7 +191,7 @@ async function createMcpServerInstance() {
|
|
|
175
191
|
try {
|
|
176
192
|
// Register all defined Git tools (alphabetized). These calls populate the server's
|
|
177
193
|
// internal registry, making them available via MCP methods like 'tools/list'.
|
|
178
|
-
logger.debug(
|
|
194
|
+
logger.debug("Registering Git tools...", context);
|
|
179
195
|
await registerGitAddTool(server);
|
|
180
196
|
await registerGitBranchTool(server);
|
|
181
197
|
await registerGitCheckoutTool(server);
|
|
@@ -202,11 +218,11 @@ async function createMcpServerInstance() {
|
|
|
202
218
|
await registerGitWorktreeTool(server);
|
|
203
219
|
await registerGitWrapupInstructionsTool(server);
|
|
204
220
|
// Add calls to register other resources/tools here if needed in the future.
|
|
205
|
-
logger.info(
|
|
221
|
+
logger.info("Git tools registered successfully", context);
|
|
206
222
|
}
|
|
207
223
|
catch (err) {
|
|
208
224
|
// Registration is critical; log and re-throw errors.
|
|
209
|
-
logger.error(
|
|
225
|
+
logger.error("Failed to register resources/tools", {
|
|
210
226
|
...context,
|
|
211
227
|
error: err instanceof Error ? err.message : String(err),
|
|
212
228
|
stack: err instanceof Error ? err.stack : undefined, // Include stack for debugging
|
|
@@ -238,11 +254,11 @@ async function createMcpServerInstance() {
|
|
|
238
254
|
async function startTransport() {
|
|
239
255
|
// Determine the transport type from the validated configuration.
|
|
240
256
|
const transportType = config.mcpTransportType;
|
|
241
|
-
const context = { operation:
|
|
257
|
+
const context = { operation: "startTransport", transport: transportType };
|
|
242
258
|
logger.info(`Starting transport: ${transportType}`, context);
|
|
243
259
|
// --- HTTP Transport Setup ---
|
|
244
|
-
if (transportType ===
|
|
245
|
-
logger.debug(
|
|
260
|
+
if (transportType === "http") {
|
|
261
|
+
logger.debug("Delegating to startHttpTransport...", context);
|
|
246
262
|
// For HTTP, the transport layer manages its own lifecycle and potentially multiple sessions.
|
|
247
263
|
// We pass the factory function to allow the HTTP transport to create server instances as needed (per session).
|
|
248
264
|
await startHttpTransport(createMcpServerInstance, context);
|
|
@@ -250,12 +266,12 @@ async function startTransport() {
|
|
|
250
266
|
return;
|
|
251
267
|
}
|
|
252
268
|
// --- Stdio Transport Setup ---
|
|
253
|
-
if (transportType ===
|
|
254
|
-
logger.debug(
|
|
269
|
+
if (transportType === "stdio") {
|
|
270
|
+
logger.debug("Creating single McpServer instance for stdio transport...", context);
|
|
255
271
|
// For stdio, there's typically one persistent connection managed by a parent process.
|
|
256
272
|
// Create a single McpServer instance for the entire process lifetime.
|
|
257
273
|
const server = await createMcpServerInstance();
|
|
258
|
-
logger.debug(
|
|
274
|
+
logger.debug("Delegating to connectStdioTransport...", context);
|
|
259
275
|
// Connect the server instance to the stdio transport handler.
|
|
260
276
|
await connectStdioTransport(server, context);
|
|
261
277
|
// Return the server instance; the caller (main entry point) might hold onto it.
|
|
@@ -278,17 +294,17 @@ async function startTransport() {
|
|
|
278
294
|
* @returns {Promise<void | McpServer>} Resolves upon successful startup (void for http, McpServer for stdio). Rejects on critical failure.
|
|
279
295
|
*/
|
|
280
296
|
export async function initializeAndStartServer() {
|
|
281
|
-
const context = { operation:
|
|
282
|
-
logger.info(
|
|
297
|
+
const context = { operation: "initializeAndStartServer" };
|
|
298
|
+
logger.info("MCP Server initialization sequence started.", context);
|
|
283
299
|
try {
|
|
284
300
|
// Initiate the transport setup based on configuration.
|
|
285
301
|
const result = await startTransport();
|
|
286
|
-
logger.info(
|
|
302
|
+
logger.info("MCP Server initialization sequence completed successfully.", context);
|
|
287
303
|
return result;
|
|
288
304
|
}
|
|
289
305
|
catch (err) {
|
|
290
306
|
// Catch any errors that occurred during server instance creation or transport setup.
|
|
291
|
-
logger.fatal(
|
|
307
|
+
logger.fatal("Critical error during MCP server initialization.", {
|
|
292
308
|
...context,
|
|
293
309
|
error: err instanceof Error ? err.message : String(err),
|
|
294
310
|
stack: err instanceof Error ? err.stack : undefined,
|
|
@@ -296,7 +312,7 @@ export async function initializeAndStartServer() {
|
|
|
296
312
|
// Use the centralized error handler for consistent critical error reporting.
|
|
297
313
|
ErrorHandler.handleError(err, { ...context, critical: true });
|
|
298
314
|
// Exit the process with a non-zero code to indicate failure.
|
|
299
|
-
logger.info(
|
|
315
|
+
logger.info("Exiting process due to critical initialization error.", context);
|
|
300
316
|
process.exit(1);
|
|
301
317
|
}
|
|
302
318
|
}
|
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
* @fileoverview Barrel file for the gitAdd tool.
|
|
3
3
|
* Exports the registration function and state accessor initialization function.
|
|
4
4
|
*/
|
|
5
|
-
export { registerGitAddTool, initializeGitAddStateAccessors } from
|
|
5
|
+
export { registerGitAddTool, initializeGitAddStateAccessors, } from "./registration.js";
|
|
6
6
|
// Export types if needed elsewhere, e.g.:
|
|
7
7
|
// export type { GitAddInput, GitAddResult } from './logic.js';
|
|
@@ -1,20 +1,25 @@
|
|
|
1
|
-
import { exec } from
|
|
2
|
-
import { promisify } from
|
|
3
|
-
import { z } from
|
|
1
|
+
import { exec } from "child_process";
|
|
2
|
+
import { promisify } from "util";
|
|
3
|
+
import { z } from "zod";
|
|
4
4
|
// Import utils from barrel (logger from ../utils/internal/logger.js)
|
|
5
|
-
import { logger } from
|
|
5
|
+
import { logger } from "../../../utils/index.js";
|
|
6
6
|
// Import utils from barrel (RequestContext from ../utils/internal/requestContext.js)
|
|
7
|
-
import { BaseErrorCode, McpError } from
|
|
7
|
+
import { BaseErrorCode, McpError } from "../../../types-global/errors.js"; // Keep direct import for types-global
|
|
8
8
|
// Import utils from barrel (sanitization from ../utils/security/sanitization.js)
|
|
9
|
-
import { sanitization } from
|
|
9
|
+
import { sanitization } from "../../../utils/index.js";
|
|
10
10
|
const execAsync = promisify(exec);
|
|
11
11
|
// Define the input schema for the git_add tool using Zod
|
|
12
12
|
export const GitAddInputSchema = z.object({
|
|
13
|
-
path: z
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
+
files: z
|
|
20
|
+
.union([z.string().min(1), z.array(z.string().min(1))])
|
|
21
|
+
.default(".")
|
|
22
|
+
.describe("Files or patterns to stage, defaults to all changes ('.')"),
|
|
18
23
|
});
|
|
19
24
|
/**
|
|
20
25
|
* Executes the 'git add' command and returns structured JSON output.
|
|
@@ -24,16 +29,18 @@ export const GitAddInputSchema = z.object({
|
|
|
24
29
|
* @returns {Promise<GitAddResult>} A promise that resolves with the structured add result.
|
|
25
30
|
* @throws {McpError} Throws an McpError if path resolution or validation fails, or if the git command fails unexpectedly.
|
|
26
31
|
*/
|
|
27
|
-
export async function addGitFiles(input, context
|
|
28
|
-
|
|
29
|
-
const operation = 'addGitFiles';
|
|
32
|
+
export async function addGitFiles(input, context) {
|
|
33
|
+
const operation = "addGitFiles";
|
|
30
34
|
logger.debug(`Executing ${operation}`, { ...context, input });
|
|
31
35
|
let targetPath;
|
|
32
36
|
try {
|
|
33
37
|
// Resolve the target path
|
|
34
|
-
if (input.path && input.path !==
|
|
38
|
+
if (input.path && input.path !== ".") {
|
|
35
39
|
targetPath = input.path;
|
|
36
|
-
logger.debug(`Using provided path: ${targetPath}`, {
|
|
40
|
+
logger.debug(`Using provided path: ${targetPath}`, {
|
|
41
|
+
...context,
|
|
42
|
+
operation,
|
|
43
|
+
});
|
|
37
44
|
}
|
|
38
45
|
else {
|
|
39
46
|
const workingDir = context.getWorkingDirectory();
|
|
@@ -41,15 +48,29 @@ export async function addGitFiles(input, context // Add getter to context
|
|
|
41
48
|
throw new McpError(BaseErrorCode.VALIDATION_ERROR, "No path provided and no working directory set for the session.", { context, operation });
|
|
42
49
|
}
|
|
43
50
|
targetPath = workingDir;
|
|
44
|
-
logger.debug(`Using session working directory: ${targetPath}`, {
|
|
51
|
+
logger.debug(`Using session working directory: ${targetPath}`, {
|
|
52
|
+
...context,
|
|
53
|
+
operation,
|
|
54
|
+
sessionId: context.sessionId,
|
|
55
|
+
});
|
|
45
56
|
}
|
|
46
57
|
// Sanitize the resolved path
|
|
47
|
-
const sanitizedPathInfo = sanitization.sanitizePath(targetPath, {
|
|
48
|
-
|
|
58
|
+
const sanitizedPathInfo = sanitization.sanitizePath(targetPath, {
|
|
59
|
+
allowAbsolute: true,
|
|
60
|
+
});
|
|
61
|
+
logger.debug("Sanitized repository path", {
|
|
62
|
+
...context,
|
|
63
|
+
operation,
|
|
64
|
+
sanitizedPathInfo,
|
|
65
|
+
});
|
|
49
66
|
targetPath = sanitizedPathInfo.sanitizedPath; // Use the sanitized path going forward
|
|
50
67
|
}
|
|
51
68
|
catch (error) {
|
|
52
|
-
logger.error(
|
|
69
|
+
logger.error("Path resolution or sanitization failed", {
|
|
70
|
+
...context,
|
|
71
|
+
operation,
|
|
72
|
+
error,
|
|
73
|
+
});
|
|
53
74
|
if (error instanceof McpError) {
|
|
54
75
|
throw error;
|
|
55
76
|
}
|
|
@@ -61,30 +82,40 @@ export async function addGitFiles(input, context // Add getter to context
|
|
|
61
82
|
try {
|
|
62
83
|
if (Array.isArray(filesToStage)) {
|
|
63
84
|
if (filesToStage.length === 0) {
|
|
64
|
-
logger.warning(
|
|
65
|
-
filesArg =
|
|
85
|
+
logger.warning("Empty array provided for files, defaulting to staging all changes.", { ...context, operation });
|
|
86
|
+
filesArg = "."; // Default to staging all if array is empty
|
|
66
87
|
}
|
|
67
88
|
else {
|
|
68
89
|
// Quote each file path individually
|
|
69
|
-
filesArg = filesToStage
|
|
70
|
-
|
|
90
|
+
filesArg = filesToStage
|
|
91
|
+
.map((file) => {
|
|
92
|
+
const sanitizedFile = file.startsWith("-") ? `./${file}` : file; // Prefix with './' if it starts with a dash
|
|
71
93
|
return `"${sanitizedFile.replace(/"/g, '\\"')}"`; // Escape quotes within path
|
|
72
|
-
})
|
|
94
|
+
})
|
|
95
|
+
.join(" ");
|
|
73
96
|
}
|
|
74
97
|
}
|
|
75
|
-
else {
|
|
76
|
-
|
|
98
|
+
else {
|
|
99
|
+
// Single string case
|
|
100
|
+
const sanitizedFile = filesToStage.startsWith("-")
|
|
101
|
+
? `./${filesToStage}`
|
|
102
|
+
: filesToStage; // Prefix with './' if it starts with a dash
|
|
77
103
|
filesArg = `"${sanitizedFile.replace(/"/g, '\\"')}"`;
|
|
78
104
|
}
|
|
79
105
|
}
|
|
80
106
|
catch (err) {
|
|
81
|
-
logger.error(
|
|
107
|
+
logger.error("File path validation/quoting failed", {
|
|
108
|
+
...context,
|
|
109
|
+
operation,
|
|
110
|
+
files: filesToStage,
|
|
111
|
+
error: err,
|
|
112
|
+
});
|
|
82
113
|
throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Invalid file path/pattern provided: ${err instanceof Error ? err.message : String(err)}`, { context, operation, originalError: err });
|
|
83
114
|
}
|
|
84
115
|
// This check should ideally not be needed now due to the logic above
|
|
85
116
|
if (!filesArg) {
|
|
86
|
-
logger.error(
|
|
87
|
-
throw new McpError(BaseErrorCode.INTERNAL_ERROR,
|
|
117
|
+
logger.error("Internal error: filesArg is unexpectedly empty after processing.", { ...context, operation });
|
|
118
|
+
throw new McpError(BaseErrorCode.INTERNAL_ERROR, "Internal error preparing git add command.", { context, operation });
|
|
88
119
|
}
|
|
89
120
|
try {
|
|
90
121
|
// Use the resolved targetPath
|
|
@@ -93,25 +124,43 @@ export async function addGitFiles(input, context // Add getter to context
|
|
|
93
124
|
const { stdout, stderr } = await execAsync(command);
|
|
94
125
|
if (stderr) {
|
|
95
126
|
// Log stderr as warning, as 'git add' can produce warnings but still succeed.
|
|
96
|
-
logger.warning(`Git add command produced stderr`, {
|
|
127
|
+
logger.warning(`Git add command produced stderr`, {
|
|
128
|
+
...context,
|
|
129
|
+
operation,
|
|
130
|
+
stderr,
|
|
131
|
+
});
|
|
97
132
|
}
|
|
98
|
-
|
|
99
|
-
|
|
133
|
+
const filesAddedDesc = Array.isArray(filesToStage)
|
|
134
|
+
? filesToStage.join(", ")
|
|
135
|
+
: filesToStage;
|
|
136
|
+
const successMessage = `Successfully staged: ${filesAddedDesc}`;
|
|
137
|
+
logger.info(successMessage, {
|
|
138
|
+
...context,
|
|
139
|
+
operation,
|
|
140
|
+
path: targetPath,
|
|
141
|
+
files: filesToStage,
|
|
142
|
+
});
|
|
100
143
|
const reminder = "Remember to write clear, concise commit messages using the Conventional Commits format (e.g., 'feat(scope): subject').";
|
|
101
144
|
// Use statusMessage and add reminder
|
|
102
145
|
return {
|
|
103
146
|
success: true,
|
|
104
|
-
statusMessage:
|
|
105
|
-
filesStaged: filesToStage
|
|
147
|
+
statusMessage: `${successMessage}. ${reminder}`,
|
|
148
|
+
filesStaged: filesToStage,
|
|
106
149
|
};
|
|
107
150
|
}
|
|
108
151
|
catch (error) {
|
|
109
|
-
logger.error(`Failed to execute git add command`, {
|
|
110
|
-
|
|
111
|
-
|
|
152
|
+
logger.error(`Failed to execute git add command`, {
|
|
153
|
+
...context,
|
|
154
|
+
operation,
|
|
155
|
+
path: targetPath,
|
|
156
|
+
error: error.message,
|
|
157
|
+
stderr: error.stderr,
|
|
158
|
+
});
|
|
159
|
+
const errorMessage = error.stderr || error.message || "";
|
|
160
|
+
if (errorMessage.toLowerCase().includes("not a git repository")) {
|
|
112
161
|
throw new McpError(BaseErrorCode.NOT_FOUND, `Path is not a Git repository: ${targetPath}`, { context, operation, originalError: error });
|
|
113
162
|
}
|
|
114
|
-
if (errorMessage.toLowerCase().includes(
|
|
163
|
+
if (errorMessage.toLowerCase().includes("did not match any files")) {
|
|
115
164
|
// Still throw an error, but return structured info in the catch block of the registration
|
|
116
165
|
throw new McpError(BaseErrorCode.NOT_FOUND, `Specified files/patterns did not match any files in ${targetPath}: ${filesArg}`, { context, operation, originalError: error, filesStaged: filesToStage });
|
|
117
166
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
// Import utils from barrel (logger from ../utils/internal/logger.js)
|
|
2
|
-
import { logger } from
|
|
2
|
+
import { logger } from "../../../utils/index.js";
|
|
3
3
|
// Import utils from barrel (ErrorHandler from ../utils/internal/errorHandler.js)
|
|
4
|
-
import { ErrorHandler } from
|
|
4
|
+
import { ErrorHandler } from "../../../utils/index.js";
|
|
5
5
|
// Import utils from barrel (requestContextService from ../utils/internal/requestContext.js)
|
|
6
|
-
import { requestContextService } from
|
|
6
|
+
import { requestContextService } from "../../../utils/index.js";
|
|
7
7
|
// Import the result type along with the function and input schema
|
|
8
|
-
import { BaseErrorCode } from
|
|
9
|
-
import { addGitFiles, GitAddInputSchema } from
|
|
8
|
+
import { BaseErrorCode } from "../../../types-global/errors.js"; // Import BaseErrorCode
|
|
9
|
+
import { addGitFiles, GitAddInputSchema, } from "./logic.js";
|
|
10
10
|
let _getWorkingDirectory;
|
|
11
11
|
let _getSessionId;
|
|
12
12
|
/**
|
|
@@ -18,10 +18,10 @@ let _getSessionId;
|
|
|
18
18
|
export function initializeGitAddStateAccessors(getWdFn, getSidFn) {
|
|
19
19
|
_getWorkingDirectory = getWdFn;
|
|
20
20
|
_getSessionId = getSidFn;
|
|
21
|
-
logger.info(
|
|
21
|
+
logger.info("State accessors initialized for git_add tool registration.");
|
|
22
22
|
}
|
|
23
|
-
const TOOL_NAME =
|
|
24
|
-
const TOOL_DESCRIPTION =
|
|
23
|
+
const TOOL_NAME = "git_add";
|
|
24
|
+
const TOOL_DESCRIPTION = "Stages changes in the Git repository for the next commit by adding file contents to the index (staging area). Can stage specific files/patterns or all changes (default: '.'). Returns the result as a JSON object.";
|
|
25
25
|
/**
|
|
26
26
|
* Registers the git_add tool with the MCP server.
|
|
27
27
|
* Uses the high-level server.tool() method for registration, schema validation, and routing.
|
|
@@ -32,15 +32,18 @@ const TOOL_DESCRIPTION = 'Stages changes in the Git repository for the next comm
|
|
|
32
32
|
*/
|
|
33
33
|
export const registerGitAddTool = async (server) => {
|
|
34
34
|
if (!_getWorkingDirectory || !_getSessionId) {
|
|
35
|
-
throw new Error(
|
|
35
|
+
throw new Error("State accessors for git_add must be initialized before registration.");
|
|
36
36
|
}
|
|
37
|
-
const operation =
|
|
37
|
+
const operation = "registerGitAddTool";
|
|
38
38
|
const context = requestContextService.createRequestContext({ operation });
|
|
39
39
|
await ErrorHandler.tryCatch(async () => {
|
|
40
40
|
server.tool(TOOL_NAME, TOOL_DESCRIPTION, GitAddInputSchema.shape, // Provide the Zod schema shape
|
|
41
41
|
async (validatedArgs, callContext) => {
|
|
42
|
-
const toolOperation =
|
|
43
|
-
const requestContext = requestContextService.createRequestContext({
|
|
42
|
+
const toolOperation = "tool:git_add";
|
|
43
|
+
const requestContext = requestContextService.createRequestContext({
|
|
44
|
+
operation: toolOperation,
|
|
45
|
+
parentContext: callContext,
|
|
46
|
+
});
|
|
44
47
|
const sessionId = _getSessionId(requestContext);
|
|
45
48
|
const getWorkingDirectoryForSession = () => {
|
|
46
49
|
return _getWorkingDirectory(sessionId);
|
|
@@ -56,10 +59,10 @@ export const registerGitAddTool = async (server) => {
|
|
|
56
59
|
const addResult = await addGitFiles(validatedArgs, logicContext);
|
|
57
60
|
// Format the successful result as a JSON string within TextContent
|
|
58
61
|
const resultContent = {
|
|
59
|
-
type:
|
|
62
|
+
type: "text",
|
|
60
63
|
// Stringify the JSON object for the response content
|
|
61
64
|
text: JSON.stringify(addResult, null, 2), // Pretty-print JSON
|
|
62
|
-
contentType:
|
|
65
|
+
contentType: "application/json",
|
|
63
66
|
};
|
|
64
67
|
logger.info(`Tool ${TOOL_NAME} executed successfully, returning JSON`, logicContext);
|
|
65
68
|
return { content: [resultContent] };
|
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
* @fileoverview Barrel file for the git_branch tool.
|
|
3
3
|
* Exports the registration function and state accessor initialization function.
|
|
4
4
|
*/
|
|
5
|
-
export { registerGitBranchTool, initializeGitBranchStateAccessors } from
|
|
5
|
+
export { registerGitBranchTool, initializeGitBranchStateAccessors, } from "./registration.js";
|
|
6
6
|
// Export types if needed elsewhere, e.g.:
|
|
7
7
|
// export type { GitBranchInput, GitBranchResult } from './logic.js';
|