@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
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # Git MCP Server
2
2
 
3
3
  [![TypeScript](https://img.shields.io/badge/TypeScript-^5.8.3-blue.svg)](https://www.typescriptlang.org/)
4
- [![Model Context Protocol](https://img.shields.io/badge/MCP%20SDK-^1.13.2-green.svg)](https://modelcontextprotocol.io/)
5
- [![Version](https://img.shields.io/badge/Version-2.1.8-blue.svg)](./CHANGELOG.md)
4
+ [![Model Context Protocol](https://img.shields.io/badge/MCP%20SDK-^1.15.1-green.svg)](https://modelcontextprotocol.io/)
5
+ [![Version](https://img.shields.io/badge/Version-2.2.0-blue.svg)](./CHANGELOG.md)
6
6
  [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
7
7
  [![Status](https://img.shields.io/badge/Status-Stable-green.svg)](https://github.com/cyanheads/git-mcp-server/issues)
8
8
  [![GitHub](https://img.shields.io/github/stars/cyanheads/git-mcp-server?style=social)](https://github.com/cyanheads/git-mcp-server)
@@ -203,9 +203,9 @@ _Note: The `path` parameter for most tools defaults to the session's working dir
203
203
 
204
204
  ## Resources
205
205
 
206
- **MCP Resources are not implemented in this version (v2.1.4).**
206
+ **MCP Resources are not implemented in this version (v2.2.0).**
207
207
 
208
- This version focuses on the refactored Git tools implementation based on the latest `mcp-ts-template` and MCP SDK v1.13.0. Resource capabilities, previously available, have been temporarily removed during this major update.
208
+ This version focuses on the refactored Git tools implementation based on the latest `mcp-ts-template` and MCP SDK v1.15.1. Resource capabilities, previously available, have been temporarily removed during this major update.
209
209
 
210
210
  If you require MCP Resource access (e.g., for reading file content directly via the server), please use the stable **[v1.2.4 release](https://github.com/cyanheads/git-mcp-server/releases/tag/v1.2.4)**.
211
211
 
@@ -6,313 +6,154 @@
6
6
  * 3. Selects and starts the appropriate communication transport (stdio or Streamable HTTP)
7
7
  * based on configuration.
8
8
  * 4. Handles top-level error management during startup.
9
- *
10
- * MCP Specification References:
11
- * - Lifecycle: https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-03-26/basic/lifecycle.mdx
12
- * - Overview (Capabilities): https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-03-26/basic/index.mdx
13
- * - Transports: https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-03-26/basic/transports.mdx
14
9
  */
15
10
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
16
- // Import validated configuration and environment details.
17
11
  import { config, environment } from "../config/index.js";
18
- // Import core utilities: ErrorHandler, logger, requestContextService.
19
- import { ErrorHandler, logger, requestContextService, } from "../utils/index.js"; // Added RequestContext
20
- // Import registration AND state initialization functions for ALL Git tools (alphabetized)
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";
12
+ import { ErrorHandler, logger, requestContextService } from "../utils/index.js";
13
+ // Import registration functions for ALL Git tools (alphabetized)
14
+ import { registerGitAddTool } from "./tools/gitAdd/index.js";
15
+ import { registerGitBranchTool } from "./tools/gitBranch/index.js";
16
+ import { registerGitCheckoutTool } from "./tools/gitCheckout/index.js";
17
+ import { registerGitCherryPickTool } from "./tools/gitCherryPick/index.js";
18
+ import { registerGitCleanTool } from "./tools/gitClean/index.js";
19
+ import { registerGitClearWorkingDirTool } from "./tools/gitClearWorkingDir/index.js";
20
+ import { registerGitCloneTool } from "./tools/gitClone/index.js";
21
+ import { registerGitCommitTool } from "./tools/gitCommit/index.js";
22
+ import { registerGitDiffTool } from "./tools/gitDiff/index.js";
23
+ import { registerGitFetchTool } from "./tools/gitFetch/index.js";
24
+ import { registerGitInitTool } from "./tools/gitInit/index.js";
25
+ import { registerGitLogTool } from "./tools/gitLog/index.js";
26
+ import { registerGitMergeTool } from "./tools/gitMerge/index.js";
27
+ import { registerGitPullTool } from "./tools/gitPull/index.js";
28
+ import { registerGitPushTool } from "./tools/gitPush/index.js";
29
+ import { registerGitRebaseTool } from "./tools/gitRebase/index.js";
30
+ import { registerGitRemoteTool } from "./tools/gitRemote/index.js";
31
+ import { registerGitResetTool } from "./tools/gitReset/index.js";
32
+ import { registerGitSetWorkingDirTool } from "./tools/gitSetWorkingDir/index.js";
33
+ import { registerGitShowTool } from "./tools/gitShow/index.js";
34
+ import { registerGitStashTool } from "./tools/gitStash/index.js";
35
+ import { registerGitStatusTool } from "./tools/gitStatus/index.js";
36
+ import { registerGitTagTool } from "./tools/gitTag/index.js";
37
+ import { registerGitWorktreeTool } from "./tools/gitWorktree/index.js";
38
+ import { registerGitWrapupInstructionsTool } from "./tools/gitWrapupInstructions/index.js";
46
39
  // Import transport setup functions
47
40
  import { startHttpTransport } from "./transports/httpTransport.js";
48
41
  import { connectStdioTransport } from "./transports/stdioTransport.js";
49
- /**
50
- * Creates and configures a new instance of the McpServer.
51
- *
52
- * This function is central to defining the server's identity and functionality
53
- * as presented to connecting clients during the MCP initialization phase.
54
- *
55
- * MCP Spec Relevance:
56
- * - Server Identity (`serverInfo`): The `name` and `version` provided here are part
57
- * of the `ServerInformation` object returned in the `InitializeResult` message,
58
- * allowing clients to identify the server they are connected to.
59
- * - Capabilities Declaration: The `capabilities` object declares the features this
60
- * server supports, enabling clients to tailor their interactions.
61
- * - `logging: {}`: Indicates the server can receive `logging/setLevel` requests
62
- * and may send `notifications/message` log messages (handled by the logger utility).
63
- * - `resources: { listChanged: true }`: Signals that the server supports dynamic
64
- * resource lists and will send `notifications/resources/list_changed` if the
65
- * available resources change after initialization. (Currently no resources registered)
66
- * - `tools: { listChanged: true }`: Signals support for dynamic tool lists and
67
- * `notifications/tools/list_changed`.
68
- * - Resource/Tool Registration: This function calls specific registration functions
69
- * (e.g., `registerGitAdd`) which use SDK methods (`server.resource`, `server.tool`)
70
- * to make capabilities available for discovery (`resources/list`, `tools/list`) and
71
- * invocation (`resources/read`, `tools/call`).
72
- *
73
- * Design Note: This factory function is used to create server instances. For the 'stdio'
74
- * transport, it's called once. For the 'http' transport, it's passed to `startHttpTransport`
75
- * and called *per session* to ensure session isolation.
76
- *
77
- * @returns {Promise<McpServer>} A promise resolving with the configured McpServer instance.
78
- * @throws {Error} If any resource or tool registration fails.
79
- */
80
- // Removed sessionId parameter, it will be retrieved from context within tool handlers
81
42
  async function createMcpServerInstance() {
82
43
  const context = requestContextService.createRequestContext({
83
44
  operation: "createMcpServerInstance",
84
45
  });
85
46
  logger.info("Initializing MCP server instance", context);
86
- // Configure the request context service (used for correlating logs/errors).
87
47
  requestContextService.configure({
88
48
  appName: config.mcpServerName,
89
49
  appVersion: config.mcpServerVersion,
90
50
  environment,
91
51
  });
92
- // Instantiate the core McpServer using the SDK.
93
- // Provide server identity (name, version) and declare supported capabilities.
94
- // Note: Resources capability declared, but none are registered currently.
95
- logger.debug("Instantiating McpServer with capabilities", {
96
- ...context,
97
- serverInfo: {
98
- name: config.mcpServerName,
99
- version: config.mcpServerVersion,
100
- },
52
+ const server = new McpServer({ name: config.mcpServerName, version: config.mcpServerVersion }, {
101
53
  capabilities: {
102
54
  logging: {},
103
55
  resources: { listChanged: true },
104
56
  tools: { listChanged: true },
105
57
  },
106
58
  });
107
- const server = new McpServer({ name: config.mcpServerName, version: config.mcpServerVersion }, // ServerInformation part of InitializeResult
108
- {
109
- capabilities: {
110
- logging: {},
111
- resources: { listChanged: true },
112
- tools: { listChanged: true },
113
- },
114
- });
115
- // Each server instance is isolated per session. This variable will hold the
116
- // working directory for the duration of this session.
117
- let sessionWorkingDirectory = undefined;
118
- // --- Define Unified State Accessor Functions ---
119
- // These functions abstract away the transport type to get/set session state.
120
- /** Gets the session ID from the tool's execution context. */
59
+ const sessionWorkingDirectories = new Map();
60
+ const STDIO_SESSION_ID = "stdio_session"; // Constant for single-session transports
121
61
  const getSessionIdFromContext = (toolContext) => {
122
- // The RequestContext created by the tool registration wrapper should contain the sessionId.
123
62
  return toolContext?.sessionId;
124
63
  };
125
- /** Gets the working directory for the current session. */
126
64
  const getWorkingDirectory = (sessionId) => {
127
- // The working directory is now stored in a variable scoped to this server instance.
128
- // The sessionId is kept for potential logging or more complex future state management.
129
- return sessionWorkingDirectory;
65
+ const id = sessionId ?? STDIO_SESSION_ID;
66
+ return sessionWorkingDirectories.get(id);
130
67
  };
131
- /** Sets the working directory for the current session. */
132
68
  const setWorkingDirectory = (sessionId, dir) => {
133
- // The working directory is now stored in a variable scoped to this server instance.
134
- logger.debug("Setting session working directory", {
135
- ...context,
136
- sessionId,
137
- newDirectory: dir,
138
- });
139
- sessionWorkingDirectory = dir;
69
+ const id = sessionId ?? STDIO_SESSION_ID;
70
+ logger.debug("Setting session working directory", { ...context, sessionId: id, newDirectory: dir });
71
+ sessionWorkingDirectories.set(id, dir);
72
+ };
73
+ const clearWorkingDirectory = (sessionId) => {
74
+ const id = sessionId ?? STDIO_SESSION_ID;
75
+ logger.debug("Clearing session working directory", { ...context, sessionId: id });
76
+ sessionWorkingDirectories.delete(id);
140
77
  };
141
- // --- Initialize Tool State Accessors BEFORE Registration ---
142
- // Pass the defined unified accessor functions to the initializers.
143
- logger.debug("Initializing state accessors for tools...", context);
144
- try {
145
- // Call initializers for all tools that likely need state access (alphabetized).
146
- // If an initializer doesn't exist, the import would have failed earlier (or build will fail).
147
- initializeGitAddStateAccessors(getWorkingDirectory, getSessionIdFromContext);
148
- initializeGitBranchStateAccessors(getWorkingDirectory, getSessionIdFromContext);
149
- initializeGitCheckoutStateAccessors(getWorkingDirectory, getSessionIdFromContext);
150
- initializeGitCherryPickStateAccessors(getWorkingDirectory, getSessionIdFromContext);
151
- initializeGitCleanStateAccessors(getWorkingDirectory, getSessionIdFromContext);
152
- initializeGitClearWorkingDirStateAccessors(getWorkingDirectory, getSessionIdFromContext);
153
- // initializeGitCloneStateAccessors - No initializer needed/available
154
- initializeGitCommitStateAccessors(getWorkingDirectory, getSessionIdFromContext);
155
- initializeGitDiffStateAccessors(getWorkingDirectory, getSessionIdFromContext);
156
- initializeGitFetchStateAccessors(getWorkingDirectory, getSessionIdFromContext);
157
- initializeGitInitStateAccessors(getWorkingDirectory, getSessionIdFromContext);
158
- initializeGitLogStateAccessors(getWorkingDirectory, getSessionIdFromContext);
159
- initializeGitMergeStateAccessors(getWorkingDirectory, getSessionIdFromContext);
160
- initializeGitPullStateAccessors(getWorkingDirectory, getSessionIdFromContext);
161
- initializeGitPushStateAccessors(getWorkingDirectory, getSessionIdFromContext);
162
- initializeGitRebaseStateAccessors(getWorkingDirectory, getSessionIdFromContext);
163
- initializeGitRemoteStateAccessors(getWorkingDirectory, getSessionIdFromContext);
164
- initializeGitResetStateAccessors(getWorkingDirectory, getSessionIdFromContext);
165
- initializeGitSetWorkingDirStateAccessors(getWorkingDirectory, setWorkingDirectory, getSessionIdFromContext); // Special case
166
- initializeGitShowStateAccessors(getWorkingDirectory, getSessionIdFromContext);
167
- initializeGitStashStateAccessors(getWorkingDirectory, getSessionIdFromContext);
168
- initializeGitStatusStateAccessors(getWorkingDirectory, getSessionIdFromContext);
169
- initializeGitTagStateAccessors(getWorkingDirectory, getSessionIdFromContext);
170
- initializeGitWorktreeStateAccessors(getWorkingDirectory, getSessionIdFromContext);
171
- initializeGitWrapupInstructionsStateAccessors(getWorkingDirectory, getSessionIdFromContext); // Added this line
172
- logger.debug("State accessors initialized successfully.", context);
173
- }
174
- catch (initError) {
175
- // Catch errors specifically during initialization phase
176
- logger.error("Failed during state accessor initialization", {
177
- ...context,
178
- error: initError instanceof Error ? initError.message : String(initError),
179
- stack: initError instanceof Error ? initError.stack : undefined,
180
- });
181
- throw initError; // Re-throw to prevent server starting incorrectly
182
- }
183
78
  try {
184
- // Register all defined Git tools (alphabetized). These calls populate the server's
185
- // internal registry, making them available via MCP methods like 'tools/list'.
186
79
  logger.debug("Registering Git tools...", context);
187
- await registerGitAddTool(server);
188
- await registerGitBranchTool(server);
189
- await registerGitCheckoutTool(server);
190
- await registerGitCherryPickTool(server);
191
- await registerGitCleanTool(server);
192
- await registerGitClearWorkingDirTool(server);
193
- await registerGitCloneTool(server);
194
- await registerGitCommitTool(server);
195
- await registerGitDiffTool(server);
196
- await registerGitFetchTool(server);
197
- await registerGitInitTool(server);
198
- await registerGitLogTool(server);
199
- await registerGitMergeTool(server);
200
- await registerGitPullTool(server);
201
- await registerGitPushTool(server);
202
- await registerGitRebaseTool(server);
203
- await registerGitRemoteTool(server);
204
- await registerGitResetTool(server);
205
- await registerGitSetWorkingDirTool(server);
206
- await registerGitShowTool(server);
207
- await registerGitStashTool(server);
208
- await registerGitStatusTool(server);
209
- await registerGitTagTool(server);
210
- await registerGitWorktreeTool(server);
211
- await registerGitWrapupInstructionsTool(server);
212
- // Add calls to register other resources/tools here if needed in the future.
80
+ await registerGitAddTool(server, getWorkingDirectory, getSessionIdFromContext);
81
+ await registerGitBranchTool(server, getWorkingDirectory, getSessionIdFromContext);
82
+ await registerGitCheckoutTool(server, getWorkingDirectory, getSessionIdFromContext);
83
+ await registerGitCherryPickTool(server, getWorkingDirectory, getSessionIdFromContext);
84
+ await registerGitCleanTool(server, getWorkingDirectory, getSessionIdFromContext);
85
+ await registerGitClearWorkingDirTool(server, clearWorkingDirectory, getSessionIdFromContext);
86
+ await registerGitCloneTool(server, getSessionIdFromContext);
87
+ await registerGitCommitTool(server, getWorkingDirectory, getSessionIdFromContext);
88
+ await registerGitDiffTool(server, getWorkingDirectory, getSessionIdFromContext);
89
+ await registerGitFetchTool(server, getWorkingDirectory, getSessionIdFromContext);
90
+ await registerGitInitTool(server, getSessionIdFromContext);
91
+ await registerGitLogTool(server, getWorkingDirectory, getSessionIdFromContext);
92
+ await registerGitMergeTool(server, getWorkingDirectory, getSessionIdFromContext);
93
+ await registerGitPullTool(server, getWorkingDirectory, getSessionIdFromContext);
94
+ await registerGitPushTool(server, getWorkingDirectory, getSessionIdFromContext);
95
+ await registerGitRebaseTool(server, getWorkingDirectory, getSessionIdFromContext);
96
+ await registerGitRemoteTool(server, getWorkingDirectory, getSessionIdFromContext);
97
+ await registerGitResetTool(server, getWorkingDirectory, getSessionIdFromContext);
98
+ await registerGitSetWorkingDirTool(server, setWorkingDirectory, getSessionIdFromContext);
99
+ await registerGitShowTool(server, getWorkingDirectory, getSessionIdFromContext);
100
+ await registerGitStashTool(server, getWorkingDirectory, getSessionIdFromContext);
101
+ await registerGitStatusTool(server, getWorkingDirectory, getSessionIdFromContext);
102
+ await registerGitTagTool(server, getWorkingDirectory, getSessionIdFromContext);
103
+ await registerGitWorktreeTool(server, getWorkingDirectory, getSessionIdFromContext);
104
+ await registerGitWrapupInstructionsTool(server, getWorkingDirectory, getSessionIdFromContext);
213
105
  logger.info("Git tools registered successfully", context);
214
106
  }
215
107
  catch (err) {
216
- // Registration is critical; log and re-throw errors.
217
108
  logger.error("Failed to register resources/tools", {
218
109
  ...context,
219
110
  error: err instanceof Error ? err.message : String(err),
220
- stack: err instanceof Error ? err.stack : undefined, // Include stack for debugging
111
+ stack: err instanceof Error ? err.stack : undefined,
221
112
  });
222
- throw err; // Propagate error to prevent server starting with incomplete capabilities.
113
+ throw err;
223
114
  }
224
115
  return server;
225
116
  }
226
- /**
227
- * Selects, sets up, and starts the appropriate MCP transport layer based on configuration.
228
- * This function acts as the bridge between the core server logic and the communication channel.
229
- *
230
- * MCP Spec Relevance:
231
- * - Transport Selection: Reads `config.mcpTransportType` ('stdio' or 'http') to determine
232
- * which transport mechanism defined in the MCP specification to use.
233
- * - Transport Connection: Calls dedicated functions (`connectStdioTransport` or `startHttpTransport`)
234
- * which handle the specifics of establishing communication according to the chosen
235
- * transport's rules (e.g., stdin/stdout handling for 'stdio', HTTP server setup and
236
- * endpoint handling for 'http').
237
- * - Server Instance Lifecycle:
238
- * - For 'stdio', creates a single `McpServer` instance for the lifetime of the process.
239
- * - For 'http', passes the `createMcpServerInstance` factory function to `startHttpTransport`,
240
- * allowing the HTTP transport to create a new, isolated server instance for each client session,
241
- * aligning with the stateful session management described in the Streamable HTTP spec.
242
- *
243
- * @returns {Promise<McpServer | void>} Resolves with the McpServer instance for 'stdio', or void for 'http'.
244
- * @throws {Error} If the configured transport type is unsupported or if transport setup fails.
245
- */
246
117
  async function startTransport() {
247
- // Determine the transport type from the validated configuration.
248
118
  const transportType = config.mcpTransportType;
249
119
  const context = requestContextService.createRequestContext({
250
120
  operation: "startTransport",
251
121
  transport: transportType,
252
122
  });
253
123
  logger.info(`Starting transport: ${transportType}`, context);
254
- // --- HTTP Transport Setup ---
124
+ const server = await createMcpServerInstance();
255
125
  if (transportType === "http") {
256
- logger.debug("Delegating to startHttpTransport...", context);
257
- // For HTTP, the transport layer manages its own lifecycle and potentially multiple sessions.
258
- // We pass the factory function to allow the HTTP transport to create server instances as needed (per session).
259
- await startHttpTransport(createMcpServerInstance, context);
260
- // The HTTP server runs indefinitely, listening for connections, so this function returns void.
126
+ await startHttpTransport(server, context);
261
127
  return;
262
128
  }
263
- // --- Stdio Transport Setup ---
264
129
  if (transportType === "stdio") {
265
- logger.debug("Creating single McpServer instance for stdio transport...", context);
266
- // For stdio, there's typically one persistent connection managed by a parent process.
267
- // Create a single McpServer instance for the entire process lifetime.
268
- const server = await createMcpServerInstance();
269
- logger.debug("Delegating to connectStdioTransport...", context);
270
- // Connect the server instance to the stdio transport handler.
271
130
  await connectStdioTransport(server, context);
272
- // Return the server instance; the caller (main entry point) might hold onto it.
273
131
  return server;
274
132
  }
275
- // --- Unsupported Transport ---
276
- // This case should theoretically not be reached due to config validation, but acts as a safeguard.
277
133
  logger.fatal(`Unsupported transport type configured: ${transportType}`, context);
278
134
  throw new Error(`Unsupported transport type: ${transportType}. Must be 'stdio' or 'http'.`);
279
135
  }
280
- /**
281
- * Main application entry point. Initializes and starts the MCP server.
282
- *
283
- * MCP Spec Relevance:
284
- * - Orchestrates the server startup sequence, culminating in a server ready to accept
285
- * connections and process MCP messages according to the chosen transport's rules.
286
- * - Implements top-level error handling for critical startup failures, ensuring the
287
- * process exits appropriately if it cannot initialize correctly.
288
- *
289
- * @returns {Promise<void | McpServer>} Resolves upon successful startup (void for http, McpServer for stdio). Rejects on critical failure.
290
- */
291
136
  export async function initializeAndStartServer() {
292
137
  const context = requestContextService.createRequestContext({
293
138
  operation: "initializeAndStartServer",
294
139
  });
295
140
  logger.info("MCP Server initialization sequence started.", context);
296
141
  try {
297
- // Initiate the transport setup based on configuration.
298
142
  const result = await startTransport();
299
143
  logger.info("MCP Server initialization sequence completed successfully.", context);
300
144
  return result;
301
145
  }
302
146
  catch (err) {
303
- // Catch any errors that occurred during server instance creation or transport setup.
304
147
  logger.fatal("Critical error during MCP server initialization.", {
305
148
  ...context,
306
149
  error: err instanceof Error ? err.message : String(err),
307
150
  stack: err instanceof Error ? err.stack : undefined,
308
151
  });
309
- // Use the centralized error handler for consistent critical error reporting.
310
152
  ErrorHandler.handleError(err, {
311
153
  ...context,
312
154
  operation: "initializeAndStartServer_Catch",
313
155
  critical: true,
314
156
  });
315
- // Exit the process with a non-zero code to indicate failure.
316
157
  logger.info("Exiting process due to critical initialization error.", context);
317
158
  process.exit(1);
318
159
  }
@@ -1,7 +1,5 @@
1
1
  /**
2
2
  * @fileoverview Barrel file for the gitAdd tool.
3
- * Exports the registration function and state accessor initialization function.
3
+ * @module src/mcp-server/tools/gitAdd/index
4
4
  */
5
- export { registerGitAddTool, initializeGitAddStateAccessors, } from "./registration.js";
6
- // Export types if needed elsewhere, e.g.:
7
- // export type { GitAddInput, GitAddResult } from './logic.js';
5
+ export { registerGitAddTool } from "./registration.js";
@@ -1,14 +1,9 @@
1
1
  import { execFile } from "child_process";
2
2
  import { promisify } from "util";
3
3
  import { z } from "zod";
4
- // Import utils from barrel (logger from ../utils/internal/logger.js)
5
- import { logger } from "../../../utils/index.js";
6
- // Import utils from barrel (RequestContext from ../utils/internal/requestContext.js)
7
- import { BaseErrorCode, McpError } from "../../../types-global/errors.js"; // Keep direct import for types-global
8
- // Import utils from barrel (sanitization from ../utils/security/sanitization.js)
9
- import { sanitization } from "../../../utils/index.js";
4
+ import { logger, sanitization } from "../../../utils/index.js";
5
+ import { BaseErrorCode, McpError } from "../../../types-global/errors.js";
10
6
  const execFileAsync = promisify(execFile);
11
- // Define the input schema for the git_add tool using Zod
12
7
  export const GitAddInputSchema = z.object({
13
8
  path: z
14
9
  .string()
@@ -21,80 +16,31 @@ export const GitAddInputSchema = z.object({
21
16
  .default(".")
22
17
  .describe("Files or patterns to stage, defaults to all changes ('.')"),
23
18
  });
24
- /**
25
- * Executes the 'git add' command and returns structured JSON output.
26
- *
27
- * @param {GitAddInput} input - The validated input object.
28
- * @param {RequestContext} context - The request context for logging and error handling.
29
- * @returns {Promise<GitAddResult>} A promise that resolves with the structured add result.
30
- * @throws {McpError} Throws an McpError if path resolution or validation fails, or if the git command fails unexpectedly.
31
- */
32
- export async function addGitFiles(input, context) {
19
+ export const GitAddOutputSchema = z.object({
20
+ success: z.boolean().describe("Indicates whether the operation was successful."),
21
+ statusMessage: z.string().describe("A message describing the result of the operation."),
22
+ filesStaged: z.union([z.string(), z.array(z.string())]).describe("The files or patterns that were staged."),
23
+ });
24
+ export async function addGitFiles(params, context) {
33
25
  const operation = "addGitFiles";
34
- logger.debug(`Executing ${operation}`, { ...context, input });
35
- let targetPath;
36
- try {
37
- // Resolve the target path
38
- if (input.path && input.path !== ".") {
39
- targetPath = input.path;
40
- logger.debug(`Using provided path: ${targetPath}`, {
41
- ...context,
42
- operation,
43
- });
44
- }
45
- else {
46
- const workingDir = context.getWorkingDirectory();
47
- if (!workingDir) {
48
- throw new McpError(BaseErrorCode.VALIDATION_ERROR, "No path provided and no working directory set for the session.", { context, operation });
49
- }
50
- targetPath = workingDir;
51
- logger.debug(`Using session working directory: ${targetPath}`, {
52
- ...context,
53
- operation,
54
- sessionId: context.sessionId,
55
- });
56
- }
57
- // Sanitize the resolved path
58
- const sanitizedPathInfo = sanitization.sanitizePath(targetPath, {
59
- allowAbsolute: true,
60
- });
61
- logger.debug("Sanitized repository path", {
62
- ...context,
63
- operation,
64
- sanitizedPathInfo,
65
- });
66
- targetPath = sanitizedPathInfo.sanitizedPath; // Use the sanitized path going forward
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
- }
77
- throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Invalid path: ${error instanceof Error ? error.message : String(error)}`, { context, operation, originalError: error });
26
+ logger.debug(`Executing ${operation}`, { ...context, params });
27
+ const workingDir = context.getWorkingDirectory();
28
+ if (params.path === "." && !workingDir) {
29
+ throw new McpError(BaseErrorCode.VALIDATION_ERROR, "No session working directory set. Please specify a 'path' or use 'git_set_working_dir' first.");
78
30
  }
79
- const filesToStage = Array.isArray(input.files) ? input.files : [input.files];
31
+ const targetPath = sanitization.sanitizePath(params.path === "." ? workingDir : params.path, { allowAbsolute: true }).sanitizedPath;
32
+ const filesToStage = Array.isArray(params.files) ? params.files : [params.files];
80
33
  if (filesToStage.length === 0) {
81
- filesToStage.push("."); // Default to staging all if array is empty
34
+ filesToStage.push(".");
82
35
  }
83
36
  try {
84
- const args = ["-C", targetPath, "add", "--"];
85
- filesToStage.forEach((file) => {
86
- // Sanitize each file path. Although execFile is safer,
87
- // this prevents arguments like "-v" from being treated as flags by git.
88
- const sanitizedFile = file.startsWith("-") ? `./${file}` : file;
89
- args.push(sanitizedFile);
90
- });
37
+ const args = ["-C", targetPath, "add", "--", ...filesToStage.map(file => file.startsWith("-") ? `./${file}` : file)];
91
38
  logger.debug(`Executing command: git ${args.join(" ")}`, {
92
39
  ...context,
93
40
  operation,
94
41
  });
95
- const { stdout, stderr } = await execFileAsync("git", args);
42
+ const { stderr } = await execFileAsync("git", args);
96
43
  if (stderr) {
97
- // Log stderr as warning, as 'git add' can produce warnings but still succeed.
98
44
  logger.warning(`Git add command produced stderr`, {
99
45
  ...context,
100
46
  operation,
@@ -112,7 +58,6 @@ export async function addGitFiles(input, context) {
112
58
  files: filesToStage,
113
59
  });
114
60
  const reminder = "Remember to write clear, concise commit messages using the Conventional Commits format (e.g., 'feat(scope): subject').";
115
- // Use statusMessage and add reminder
116
61
  return {
117
62
  success: true,
118
63
  statusMessage: `${successMessage}. ${reminder}`,
@@ -132,10 +77,8 @@ export async function addGitFiles(input, context) {
132
77
  throw new McpError(BaseErrorCode.NOT_FOUND, `Path is not a Git repository: ${targetPath}`, { context, operation, originalError: error });
133
78
  }
134
79
  if (errorMessage.toLowerCase().includes("did not match any files")) {
135
- // Still throw an error, but return structured info in the catch block of the registration
136
80
  throw new McpError(BaseErrorCode.NOT_FOUND, `Specified files/patterns did not match any files in ${targetPath}: ${filesToStage.join(", ")}`, { context, operation, originalError: error, filesStaged: filesToStage });
137
81
  }
138
- // Throw generic error for other cases
139
82
  throw new McpError(BaseErrorCode.INTERNAL_ERROR, `Failed to stage files for path: ${targetPath}. Error: ${errorMessage}`, { context, operation, originalError: error, filesStaged: filesToStage });
140
83
  }
141
84
  }