@cyanheads/git-mcp-server 2.0.15 → 2.1.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 +1 -1
- package/dist/mcp-server/server.js +2 -2
- package/dist/mcp-server/tools/gitStatus/logic.js +61 -58
- package/dist/mcp-server/tools/gitStatus/registration.js +1 -1
- package/dist/mcp-server/tools/gitWrapupInstructions/index.js +3 -3
- package/dist/mcp-server/tools/gitWrapupInstructions/logic.js +29 -2
- package/dist/mcp-server/tools/gitWrapupInstructions/registration.js +50 -14
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.typescriptlang.org/)
|
|
4
4
|
[](https://modelcontextprotocol.io/)
|
|
5
|
-
[](./CHANGELOG.md)
|
|
6
6
|
[](https://opensource.org/licenses/Apache-2.0)
|
|
7
7
|
[](https://github.com/cyanheads/git-mcp-server/issues)
|
|
8
8
|
[](https://github.com/cyanheads/git-mcp-server)
|
|
@@ -42,7 +42,7 @@ import { initializeGitStashStateAccessors, registerGitStashTool } from './tools/
|
|
|
42
42
|
import { initializeGitStatusStateAccessors, registerGitStatusTool } from './tools/gitStatus/index.js';
|
|
43
43
|
import { initializeGitTagStateAccessors, registerGitTagTool } from './tools/gitTag/index.js';
|
|
44
44
|
import { initializeGitWorktreeStateAccessors, registerGitWorktreeTool } from './tools/gitWorktree/index.js';
|
|
45
|
-
import { registerGitWrapupInstructionsTool } from './tools/gitWrapupInstructions/index.js';
|
|
45
|
+
import { initializeGitWrapupInstructionsStateAccessors, registerGitWrapupInstructionsTool } from './tools/gitWrapupInstructions/index.js';
|
|
46
46
|
// Import transport setup functions AND state accessors
|
|
47
47
|
import { getHttpSessionWorkingDirectory, setHttpSessionWorkingDirectory, startHttpTransport } from './transports/httpTransport.js';
|
|
48
48
|
import { connectStdioTransport, getStdioWorkingDirectory, setStdioWorkingDirectory } from './transports/stdioTransport.js';
|
|
@@ -160,7 +160,7 @@ async function createMcpServerInstance() {
|
|
|
160
160
|
initializeGitStatusStateAccessors(getWorkingDirectory, getSessionIdFromContext);
|
|
161
161
|
initializeGitTagStateAccessors(getWorkingDirectory, getSessionIdFromContext);
|
|
162
162
|
initializeGitWorktreeStateAccessors(getWorkingDirectory, getSessionIdFromContext);
|
|
163
|
-
|
|
163
|
+
initializeGitWrapupInstructionsStateAccessors(getWorkingDirectory, getSessionIdFromContext); // Added this line
|
|
164
164
|
logger.debug('State accessors initialized successfully.', context);
|
|
165
165
|
}
|
|
166
166
|
catch (initError) {
|
|
@@ -18,72 +18,61 @@ export const GitStatusInputSchema = z.object({
|
|
|
18
18
|
function parseGitStatusPorcelainV1(porcelainOutput) {
|
|
19
19
|
const lines = porcelainOutput.trim().split('\n');
|
|
20
20
|
const result = {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
21
|
+
current_branch: null,
|
|
22
|
+
staged_changes: {},
|
|
23
|
+
unstaged_changes: {},
|
|
24
|
+
untracked_files: [],
|
|
25
|
+
conflicted_files: [],
|
|
26
|
+
is_clean: true, // Assume clean initially
|
|
27
27
|
};
|
|
28
28
|
if (lines.length === 0 || (lines.length === 1 && lines[0] === '')) {
|
|
29
|
-
// If output is empty, it might mean no branch yet or truly clean
|
|
30
|
-
// We'll refine branch detection below if possible
|
|
31
29
|
return result;
|
|
32
30
|
}
|
|
33
|
-
// First line often contains branch info (e.g., ## master...origin/master)
|
|
34
31
|
if (lines[0].startsWith('## ')) {
|
|
35
|
-
const branchLine = lines.shift();
|
|
36
|
-
// Try matching standard branch format first (e.g., ## master...origin/master [ahead 1])
|
|
37
|
-
// Make regex more specific: look for '...' or '[' after branch name, or end of line for simple branch name
|
|
32
|
+
const branchLine = lines.shift();
|
|
38
33
|
const standardBranchMatch = branchLine.match(/^## ([^ ]+?)(?:\.\.\.| \[.*\]|$)/);
|
|
39
|
-
// Try matching the 'No commits yet' format (e.g., ## No commits yet on master)
|
|
40
34
|
const noCommitsMatch = branchLine.match(/^## No commits yet on (.+)/);
|
|
41
|
-
// Try matching detached HEAD format (e.g., ## HEAD (no branch))
|
|
42
35
|
const detachedMatch = branchLine.match(/^## HEAD \(no branch\)/);
|
|
43
36
|
if (standardBranchMatch) {
|
|
44
|
-
result.
|
|
45
|
-
// TODO: Optionally parse ahead/behind counts if needed from the full match
|
|
37
|
+
result.current_branch = standardBranchMatch[1];
|
|
46
38
|
}
|
|
47
39
|
else if (noCommitsMatch) {
|
|
48
|
-
|
|
49
|
-
result.currentBranch = `${noCommitsMatch[1]} (no commits yet)`;
|
|
40
|
+
result.current_branch = `${noCommitsMatch[1]} (no commits yet)`;
|
|
50
41
|
}
|
|
51
|
-
else if (detachedMatch) {
|
|
52
|
-
result.
|
|
42
|
+
else if (detachedMatch) {
|
|
43
|
+
result.current_branch = 'HEAD (detached)';
|
|
53
44
|
}
|
|
54
45
|
else {
|
|
55
|
-
// Fallback if branch line format is unexpected
|
|
56
46
|
logger.warning('Could not parse branch information from line:', { branchLine });
|
|
57
|
-
result.
|
|
47
|
+
result.current_branch = '(unknown)';
|
|
58
48
|
}
|
|
59
49
|
}
|
|
60
50
|
for (const line of lines) {
|
|
61
51
|
if (!line)
|
|
62
|
-
continue;
|
|
63
|
-
result.
|
|
52
|
+
continue;
|
|
53
|
+
result.is_clean = false; // Any line indicates non-clean state
|
|
64
54
|
const xy = line.substring(0, 2);
|
|
65
|
-
const file = line.substring(3);
|
|
66
|
-
const
|
|
67
|
-
const
|
|
55
|
+
const file = line.substring(3);
|
|
56
|
+
const stagedStatusChar = xy[0];
|
|
57
|
+
const unstagedStatusChar = xy[1];
|
|
68
58
|
// Handle untracked files
|
|
69
59
|
if (xy === '??') {
|
|
70
|
-
result.
|
|
60
|
+
result.untracked_files.push(file);
|
|
71
61
|
continue;
|
|
72
62
|
}
|
|
73
|
-
// Handle conflicted files (
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
continue;
|
|
63
|
+
// Handle conflicted files (unmerged paths)
|
|
64
|
+
// DD = both deleted, AU = added by us, UD = deleted by them, UA = added by them, DU = deleted by us
|
|
65
|
+
// AA = both added, UU = both modified
|
|
66
|
+
if (stagedStatusChar === 'U' || unstagedStatusChar === 'U' ||
|
|
67
|
+
(stagedStatusChar === 'D' && unstagedStatusChar === 'D') ||
|
|
68
|
+
(stagedStatusChar === 'A' && unstagedStatusChar === 'A')) {
|
|
69
|
+
result.conflicted_files.push(file);
|
|
70
|
+
continue; // Conflicted files are handled separately and not in staged/unstaged
|
|
82
71
|
}
|
|
83
72
|
// Handle staged changes (index status)
|
|
84
|
-
if (
|
|
85
|
-
let statusDesc =
|
|
86
|
-
switch (
|
|
73
|
+
if (stagedStatusChar !== ' ' && stagedStatusChar !== '?') {
|
|
74
|
+
let statusDesc = undefined;
|
|
75
|
+
switch (stagedStatusChar) {
|
|
87
76
|
case 'M':
|
|
88
77
|
statusDesc = 'Modified';
|
|
89
78
|
break;
|
|
@@ -95,20 +84,25 @@ function parseGitStatusPorcelainV1(porcelainOutput) {
|
|
|
95
84
|
break;
|
|
96
85
|
case 'R':
|
|
97
86
|
statusDesc = 'Renamed';
|
|
98
|
-
break;
|
|
87
|
+
break;
|
|
99
88
|
case 'C':
|
|
100
89
|
statusDesc = 'Copied';
|
|
101
|
-
break;
|
|
90
|
+
break;
|
|
102
91
|
case 'T':
|
|
103
|
-
statusDesc = '
|
|
92
|
+
statusDesc = 'TypeChanged';
|
|
104
93
|
break;
|
|
105
94
|
}
|
|
106
|
-
|
|
95
|
+
if (statusDesc) {
|
|
96
|
+
if (!result.staged_changes[statusDesc]) {
|
|
97
|
+
result.staged_changes[statusDesc] = [];
|
|
98
|
+
}
|
|
99
|
+
result.staged_changes[statusDesc].push(file);
|
|
100
|
+
}
|
|
107
101
|
}
|
|
108
102
|
// Handle unstaged changes (worktree status)
|
|
109
|
-
if (
|
|
110
|
-
let statusDesc =
|
|
111
|
-
switch (
|
|
103
|
+
if (unstagedStatusChar !== ' ' && unstagedStatusChar !== '?') {
|
|
104
|
+
let statusDesc = undefined;
|
|
105
|
+
switch (unstagedStatusChar) {
|
|
112
106
|
case 'M':
|
|
113
107
|
statusDesc = 'Modified';
|
|
114
108
|
break;
|
|
@@ -116,18 +110,23 @@ function parseGitStatusPorcelainV1(porcelainOutput) {
|
|
|
116
110
|
statusDesc = 'Deleted';
|
|
117
111
|
break;
|
|
118
112
|
case 'T':
|
|
119
|
-
statusDesc = '
|
|
113
|
+
statusDesc = 'TypeChanged';
|
|
120
114
|
break;
|
|
121
|
-
//
|
|
115
|
+
// 'A' (Added but not committed) is handled by '??' (untracked)
|
|
116
|
+
// 'R' and 'C' in worktree without being staged are complex, often appear as deleted + untracked
|
|
122
117
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
118
|
+
if (statusDesc) {
|
|
119
|
+
if (!result.unstaged_changes[statusDesc]) {
|
|
120
|
+
result.unstaged_changes[statusDesc] = [];
|
|
121
|
+
}
|
|
122
|
+
result.unstaged_changes[statusDesc].push(file);
|
|
126
123
|
}
|
|
127
124
|
}
|
|
128
125
|
}
|
|
129
|
-
|
|
130
|
-
|
|
126
|
+
result.is_clean = Object.keys(result.staged_changes).length === 0 &&
|
|
127
|
+
Object.keys(result.unstaged_changes).length === 0 &&
|
|
128
|
+
result.untracked_files.length === 0 &&
|
|
129
|
+
result.conflicted_files.length === 0;
|
|
131
130
|
return result;
|
|
132
131
|
}
|
|
133
132
|
/**
|
|
@@ -186,13 +185,17 @@ export async function getGitStatus(input, context // Add getter to context
|
|
|
186
185
|
const structuredResult = parseGitStatusPorcelainV1(stdout);
|
|
187
186
|
// If parsing resulted in clean state but no branch, re-check branch explicitly
|
|
188
187
|
// This handles the case of an empty repo after init but before first commit
|
|
189
|
-
if (structuredResult.
|
|
188
|
+
if (structuredResult.is_clean && !structuredResult.current_branch) {
|
|
190
189
|
try {
|
|
191
190
|
const branchCommand = `git -C "${targetPath}" rev-parse --abbrev-ref HEAD`;
|
|
192
191
|
const { stdout: branchStdout } = await execAsync(branchCommand);
|
|
193
|
-
const
|
|
194
|
-
if (
|
|
195
|
-
structuredResult.
|
|
192
|
+
const currentBranchName = branchStdout.trim(); // Renamed variable for clarity
|
|
193
|
+
if (currentBranchName && currentBranchName !== 'HEAD') {
|
|
194
|
+
structuredResult.current_branch = currentBranchName;
|
|
195
|
+
}
|
|
196
|
+
else if (currentBranchName === 'HEAD' && !structuredResult.current_branch) {
|
|
197
|
+
// If rev-parse returns HEAD and we still don't have a branch (e.g. detached from no-commits branch)
|
|
198
|
+
structuredResult.current_branch = 'HEAD (detached)';
|
|
196
199
|
}
|
|
197
200
|
}
|
|
198
201
|
catch (branchError) {
|
|
@@ -16,7 +16,7 @@ export function initializeGitStatusStateAccessors(getWdFn, getSidFn) {
|
|
|
16
16
|
logger.info('State accessors initialized for git_status tool registration.');
|
|
17
17
|
}
|
|
18
18
|
const TOOL_NAME = 'git_status';
|
|
19
|
-
const TOOL_DESCRIPTION = 'Retrieves the status of a Git repository.
|
|
19
|
+
const TOOL_DESCRIPTION = 'Retrieves the status of a Git repository. Returns a JSON object detailing the current branch, cleanliness, and changes. Staged and unstaged changes are grouped by status (e.g., Added, Modified), alongside lists of untracked and conflicted files.';
|
|
20
20
|
/**
|
|
21
21
|
* Registers the git_status tool with the MCP server.
|
|
22
22
|
* Uses the high-level server.tool() method for registration, schema validation, and routing.
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { registerGitWrapupInstructionsTool } from './registration.js';
|
|
2
|
-
// This tool
|
|
3
|
-
//
|
|
1
|
+
export { registerGitWrapupInstructionsTool, initializeGitWrapupInstructionsStateAccessors } from './registration.js';
|
|
2
|
+
// This tool now requires session-specific state accessors (getWorkingDirectory, getSessionId)
|
|
3
|
+
// to fetch git status, so initializeGitWrapupInstructionsStateAccessors is exported for server setup.
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { logger } from '../../../utils/index.js'; // Added logger
|
|
3
|
+
import { getGitStatus } from '../gitStatus/logic.js'; // Corrected path
|
|
2
4
|
// Define the input schema
|
|
3
5
|
export const GitWrapupInstructionsInputSchema = z.object({
|
|
4
6
|
acknowledgement: z.enum(['Y', 'y', 'Yes', 'yes'], {
|
|
@@ -15,6 +17,8 @@ Perform all actions for our git wrapup workflow:
|
|
|
15
17
|
3. Update the CHANGELOG with concise, descriptive entries detailing all modifications, clearly indicating their purpose (e.g., bug fix, feature implementation, refactoring). Include specific metrics or identifiers where applicable, such as issue numbers or pull request links, to provide context and traceability for each change. This will help maintain a clear history of changes and their impacts on the project.
|
|
16
18
|
4. Proceed to commit all changes; based on your review of the git_diff and readme, group these changes into logical, atomic commits, each accompanied by a clear and descriptive message adhering to Conventional Commits standards (e.g. "docs(readme): updated readme to include xyz."). Note the 'git_commit' tool allows you to also stage the files while commiting. Ensure commit messages accurately convey the scope and impact of the changes, incorporating specific metrics or identifiers where applicable.
|
|
17
19
|
Note: Be sure to set 'git_set_working_dir' if not already set.
|
|
20
|
+
|
|
21
|
+
Instructions: Now write a concise list of what you must do to complete the git wrapup workflow, then perform all actions. Do not push unless requested.
|
|
18
22
|
`;
|
|
19
23
|
/**
|
|
20
24
|
* Core logic for the git_wrapup_instructions tool.
|
|
@@ -24,13 +28,36 @@ Note: Be sure to set 'git_set_working_dir' if not already set.
|
|
|
24
28
|
* @param {RequestContext} _context - The request context (included for consistency, not used in this simple logic).
|
|
25
29
|
* @returns {Promise<GitWrapupInstructionsResult>} A promise that resolves with the wrap-up instructions.
|
|
26
30
|
*/
|
|
27
|
-
export async function getWrapupInstructions(input,
|
|
28
|
-
|
|
31
|
+
export async function getWrapupInstructions(input,
|
|
32
|
+
// The context is now expected to be enhanced by the registration layer
|
|
33
|
+
// to include session-specific methods like getWorkingDirectory.
|
|
34
|
+
context) {
|
|
29
35
|
let finalInstructions = WRAPUP_INSTRUCTIONS;
|
|
30
36
|
if (input.updateAgentMetaFiles && ['Y', 'y', 'Yes', 'yes'].includes(input.updateAgentMetaFiles)) {
|
|
31
37
|
finalInstructions += ` Extra request: review and update if needed the .clinerules and claude.md files if present.`;
|
|
32
38
|
}
|
|
39
|
+
let statusResult = undefined;
|
|
40
|
+
let statusError = undefined;
|
|
41
|
+
const workingDir = context.getWorkingDirectory();
|
|
42
|
+
if (workingDir) {
|
|
43
|
+
try {
|
|
44
|
+
// The `getGitStatus` function expects `path` and a context with `getWorkingDirectory`.
|
|
45
|
+
// Passing `path: '.'` signals `getGitStatus` to use the working directory from the context.
|
|
46
|
+
// The `registration.ts` for this tool will be responsible for ensuring `context.getWorkingDirectory` is correctly supplied.
|
|
47
|
+
statusResult = await getGitStatus({ path: '.' }, context);
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
logger.warning(`Failed to get git status while generating wrapup instructions (working dir: ${workingDir}). Tool will proceed without it.`, { ...context, tool: 'gitWrapupInstructions', originalError: error.message });
|
|
51
|
+
statusError = error instanceof Error ? error.message : String(error);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
logger.info('No working directory set for session, skipping git status for wrapup instructions.', { ...context, tool: 'gitWrapupInstructions' });
|
|
56
|
+
statusError = 'No working directory set for session, git status skipped.';
|
|
57
|
+
}
|
|
33
58
|
return {
|
|
34
59
|
instructions: finalInstructions,
|
|
60
|
+
gitStatus: statusResult,
|
|
61
|
+
gitStatusError: statusError,
|
|
35
62
|
};
|
|
36
63
|
}
|
|
@@ -1,42 +1,78 @@
|
|
|
1
1
|
import { BaseErrorCode } from '../../../types-global/errors.js';
|
|
2
2
|
import { ErrorHandler, logger, requestContextService } from '../../../utils/index.js';
|
|
3
3
|
import { getWrapupInstructions, GitWrapupInstructionsInputSchema, } from './logic.js';
|
|
4
|
+
let _getWorkingDirectory;
|
|
5
|
+
let _getSessionId;
|
|
6
|
+
/**
|
|
7
|
+
* Initializes the state accessors needed by the git_wrapup_instructions tool.
|
|
8
|
+
* This should be called once during server setup by server.ts.
|
|
9
|
+
* @param getWdFn - Function to get the working directory for a session.
|
|
10
|
+
* @param getSidFn - Function to get the session ID from context.
|
|
11
|
+
*/
|
|
12
|
+
export function initializeGitWrapupInstructionsStateAccessors(getWdFn, getSidFn) {
|
|
13
|
+
_getWorkingDirectory = getWdFn;
|
|
14
|
+
_getSessionId = getSidFn;
|
|
15
|
+
logger.info('State accessors initialized for git_wrapup_instructions tool registration.');
|
|
16
|
+
}
|
|
4
17
|
const TOOL_NAME = 'git_wrapup_instructions';
|
|
5
|
-
const TOOL_DESCRIPTION = 'Provides a standard Git wrap-up workflow. This involves reviewing changes with `git_diff`, updating documentation (README, CHANGELOG), and making logical, descriptive commits using the `git_commit` tool.';
|
|
18
|
+
const TOOL_DESCRIPTION = 'Provides a standard Git wrap-up workflow. This involves reviewing changes with `git_diff`, updating documentation (README, CHANGELOG), and making logical, descriptive commits using the `git_commit` tool. The tool\'s response also includes the current `git status` output. You should set the working directory using `git_set_working_dir` before running this tool.';
|
|
6
19
|
/**
|
|
7
20
|
* Registers the git_wrapup_instructions tool with the MCP server.
|
|
8
21
|
*
|
|
9
22
|
* @param {McpServer} server - The McpServer instance to register the tool with.
|
|
10
23
|
* @returns {Promise<void>}
|
|
11
|
-
* @throws {Error} If registration fails.
|
|
24
|
+
* @throws {Error} If registration fails or state accessors are not initialized.
|
|
12
25
|
*/
|
|
13
26
|
export const registerGitWrapupInstructionsTool = async (server) => {
|
|
27
|
+
if (!_getWorkingDirectory || !_getSessionId) {
|
|
28
|
+
throw new Error('State accessors for git_wrapup_instructions must be initialized before registration.');
|
|
29
|
+
}
|
|
14
30
|
const operation = 'registerGitWrapupInstructionsTool';
|
|
15
|
-
|
|
31
|
+
// Context for the registration operation itself
|
|
32
|
+
const registrationOpContext = requestContextService.createRequestContext({ operation });
|
|
16
33
|
await ErrorHandler.tryCatch(async () => {
|
|
17
|
-
server.tool(TOOL_NAME, TOOL_DESCRIPTION, GitWrapupInstructionsInputSchema.shape,
|
|
18
|
-
async (validatedArgs, callContext) => {
|
|
34
|
+
server.tool(TOOL_NAME, TOOL_DESCRIPTION, GitWrapupInstructionsInputSchema.shape, async (validatedArgs, callContext) => {
|
|
19
35
|
const toolOperation = 'tool:git_wrapup_instructions';
|
|
20
|
-
//
|
|
21
|
-
|
|
22
|
-
|
|
36
|
+
// Create a base RequestContext for this specific tool call,
|
|
37
|
+
// potentially linking to the callContext provided by the McpServer.
|
|
38
|
+
// Pass callContext directly; createRequestContext will handle it appropriately
|
|
39
|
+
// (e.g., by trying to extract a requestId or sessionId if relevant for linking).
|
|
40
|
+
const baseRequestContext = requestContextService.createRequestContext({ operation: toolOperation, parentContext: callContext });
|
|
41
|
+
// Retrieve the session ID using the initialized accessor.
|
|
42
|
+
// _getSessionId (which is getSessionIdFromContext from server.ts) expects Record<string, any>.
|
|
43
|
+
// callContext from server.tool() is compatible with Record<string, any>.
|
|
44
|
+
const sessionId = _getSessionId(callContext);
|
|
45
|
+
// Create the session-specific getWorkingDirectory function.
|
|
46
|
+
const getWorkingDirectoryForSession = () => {
|
|
47
|
+
// _getWorkingDirectory is guaranteed to be defined by the check at the start of register function.
|
|
48
|
+
return _getWorkingDirectory(sessionId);
|
|
49
|
+
};
|
|
50
|
+
// Construct the logicContext to be passed to the tool's core logic.
|
|
51
|
+
// This includes the base request context properties, the session ID,
|
|
52
|
+
// and the specific getWorkingDirectory function for this session.
|
|
53
|
+
const logicContext = {
|
|
54
|
+
...baseRequestContext,
|
|
55
|
+
sessionId: sessionId,
|
|
56
|
+
getWorkingDirectory: getWorkingDirectoryForSession,
|
|
57
|
+
};
|
|
58
|
+
logger.info(`Executing tool: ${TOOL_NAME}`, logicContext);
|
|
23
59
|
return await ErrorHandler.tryCatch(async () => {
|
|
24
|
-
|
|
25
|
-
);
|
|
60
|
+
// Call the core logic function with validated arguments and the prepared logicContext.
|
|
61
|
+
const result = await getWrapupInstructions(validatedArgs, logicContext);
|
|
26
62
|
const resultContent = {
|
|
27
63
|
type: 'text',
|
|
28
64
|
text: JSON.stringify(result, null, 2),
|
|
29
65
|
contentType: 'application/json',
|
|
30
66
|
};
|
|
31
|
-
logger.info(`Tool ${TOOL_NAME} executed successfully, returning JSON`,
|
|
67
|
+
logger.info(`Tool ${TOOL_NAME} executed successfully, returning JSON`, logicContext);
|
|
32
68
|
return { content: [resultContent] };
|
|
33
69
|
}, {
|
|
34
70
|
operation: toolOperation,
|
|
35
|
-
context:
|
|
71
|
+
context: logicContext, // Use the enhanced logicContext for error reporting
|
|
36
72
|
input: validatedArgs,
|
|
37
73
|
errorCode: BaseErrorCode.INTERNAL_ERROR,
|
|
38
74
|
});
|
|
39
75
|
});
|
|
40
|
-
logger.info(`Tool registered: ${TOOL_NAME}`,
|
|
41
|
-
}, { operation, context, critical: true });
|
|
76
|
+
logger.info(`Tool registered: ${TOOL_NAME}`, registrationOpContext); // Use context for registration operation
|
|
77
|
+
}, { operation, context: registrationOpContext, critical: true }); // Use context for registration operation
|
|
42
78
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cyanheads/git-mcp-server",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.1",
|
|
4
4
|
"description": "An MCP (Model Context Protocol) server enabling LLMs and AI agents to interact with Git repositories. Provides tools for comprehensive Git operations including clone, commit, branch, diff, log, status, push, pull, merge, rebase, worktree, tag management, and more, via the MCP standard. STDIO & HTTP.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -37,18 +37,18 @@
|
|
|
37
37
|
"access": "public"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@modelcontextprotocol/inspector": "^0.
|
|
40
|
+
"@modelcontextprotocol/inspector": "^0.14.0",
|
|
41
41
|
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
42
42
|
"@types/jsonwebtoken": "^9.0.9",
|
|
43
|
-
"@types/node": "^
|
|
43
|
+
"@types/node": "^24.0.1",
|
|
44
44
|
"@types/sanitize-html": "^2.16.0",
|
|
45
45
|
"@types/validator": "^13.15.1",
|
|
46
46
|
"chrono-node": "2.8.0",
|
|
47
47
|
"dotenv": "^16.5.0",
|
|
48
48
|
"express": "^5.1.0",
|
|
49
|
-
"ignore": "^7.0.
|
|
49
|
+
"ignore": "^7.0.5",
|
|
50
50
|
"jsonwebtoken": "^9.0.2",
|
|
51
|
-
"openai": "^5.0
|
|
51
|
+
"openai": "^5.3.0",
|
|
52
52
|
"partial-json": "^0.1.7",
|
|
53
53
|
"sanitize-html": "^2.17.0",
|
|
54
54
|
"tiktoken": "^1.0.21",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"winston": "^3.17.0",
|
|
59
59
|
"winston-daily-rotate-file": "^5.0.0",
|
|
60
60
|
"yargs": "^18.0.0",
|
|
61
|
-
"zod": "^3.25.
|
|
61
|
+
"zod": "^3.25.64"
|
|
62
62
|
},
|
|
63
63
|
"keywords": [
|
|
64
64
|
"typescript",
|
|
@@ -96,6 +96,6 @@
|
|
|
96
96
|
"automation"
|
|
97
97
|
],
|
|
98
98
|
"devDependencies": {
|
|
99
|
-
"@types/express": "^5.0.
|
|
99
|
+
"@types/express": "^5.0.3"
|
|
100
100
|
}
|
|
101
101
|
}
|