@cyanheads/git-mcp-server 2.1.3 → 2.1.5
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 +10 -8
- package/dist/config/index.js +10 -2
- package/dist/mcp-server/server.js +33 -32
- package/dist/mcp-server/tools/gitAdd/logic.js +18 -45
- package/dist/mcp-server/tools/gitBranch/logic.js +39 -34
- package/dist/mcp-server/tools/gitCheckout/logic.js +17 -12
- package/dist/mcp-server/tools/gitCherryPick/logic.js +22 -15
- package/dist/mcp-server/tools/gitClean/logic.js +11 -8
- package/dist/mcp-server/tools/gitClone/logic.js +15 -11
- package/dist/mcp-server/tools/gitCommit/logic.js +29 -65
- package/dist/mcp-server/tools/gitDiff/logic.js +29 -14
- package/dist/mcp-server/tools/gitFetch/logic.js +13 -12
- package/dist/mcp-server/tools/gitInit/logic.js +12 -9
- package/dist/mcp-server/tools/gitLog/logic.js +17 -30
- package/dist/mcp-server/tools/gitMerge/logic.js +17 -12
- package/dist/mcp-server/tools/gitPull/logic.js +13 -14
- package/dist/mcp-server/tools/gitPush/logic.js +19 -21
- package/dist/mcp-server/tools/gitRebase/logic.js +29 -20
- package/dist/mcp-server/tools/gitRemote/logic.js +15 -15
- package/dist/mcp-server/tools/gitReset/logic.js +11 -10
- package/dist/mcp-server/tools/gitSetWorkingDir/logic.js +6 -4
- package/dist/mcp-server/tools/gitShow/logic.js +9 -8
- package/dist/mcp-server/tools/gitStash/logic.js +16 -17
- package/dist/mcp-server/tools/gitStatus/logic.js +10 -8
- package/dist/mcp-server/tools/gitTag/logic.js +15 -15
- package/dist/mcp-server/tools/gitWorktree/logic.js +54 -38
- package/dist/mcp-server/transports/auth/core/authContext.js +24 -0
- package/dist/mcp-server/transports/auth/core/authTypes.js +5 -0
- package/dist/mcp-server/transports/auth/core/authUtils.js +45 -0
- package/dist/mcp-server/transports/auth/index.js +9 -0
- package/dist/mcp-server/transports/auth/strategies/jwt/jwtMiddleware.js +149 -0
- package/dist/mcp-server/transports/auth/strategies/oauth/oauthMiddleware.js +127 -0
- package/dist/mcp-server/transports/httpErrorHandler.js +73 -0
- package/dist/mcp-server/transports/httpTransport.js +149 -495
- package/dist/mcp-server/transports/stdioTransport.js +18 -48
- package/package.json +4 -13
- package/dist/mcp-server/transports/authentication/authMiddleware.js +0 -167
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { execFile } from "child_process";
|
|
2
2
|
import { promisify } from "util";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
// Import utils from barrel (logger from ../utils/internal/logger.js)
|
|
@@ -7,7 +7,7 @@ import { logger } from "../../../utils/index.js";
|
|
|
7
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
9
|
import { sanitization } from "../../../utils/index.js";
|
|
10
|
-
const
|
|
10
|
+
const execFileAsync = promisify(execFile);
|
|
11
11
|
// Define the input schema for the git_push tool using Zod
|
|
12
12
|
export const GitPushInputSchema = z.object({
|
|
13
13
|
path: z
|
|
@@ -111,45 +111,43 @@ export async function pushGitChanges(input, context) {
|
|
|
111
111
|
}
|
|
112
112
|
try {
|
|
113
113
|
// Construct the git push command
|
|
114
|
-
|
|
114
|
+
const args = ["-C", targetPath, "push"];
|
|
115
115
|
if (input.force) {
|
|
116
|
-
|
|
116
|
+
args.push("--force");
|
|
117
117
|
}
|
|
118
118
|
else if (input.forceWithLease) {
|
|
119
|
-
|
|
119
|
+
args.push("--force-with-lease");
|
|
120
120
|
}
|
|
121
121
|
if (input.setUpstream) {
|
|
122
|
-
|
|
122
|
+
args.push("--set-upstream");
|
|
123
123
|
}
|
|
124
124
|
if (input.tags) {
|
|
125
|
-
|
|
125
|
+
args.push("--tags");
|
|
126
126
|
}
|
|
127
127
|
if (input.delete) {
|
|
128
|
-
|
|
128
|
+
args.push("--delete");
|
|
129
129
|
}
|
|
130
130
|
// Add remote and branch specification
|
|
131
|
-
const remote = input.remote
|
|
132
|
-
|
|
133
|
-
: "origin"; // Default to origin
|
|
134
|
-
command += ` ${remote}`;
|
|
131
|
+
const remote = input.remote || "origin"; // Default to origin
|
|
132
|
+
args.push(remote);
|
|
135
133
|
if (input.branch) {
|
|
136
|
-
const localBranch = input.branch.replace(/[^a-zA-Z0-9_.\-/]/g, "");
|
|
137
|
-
command += ` ${localBranch}`;
|
|
138
134
|
if (input.remoteBranch && !input.delete) {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
135
|
+
args.push(`${input.branch}:${input.remoteBranch}`);
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
args.push(input.branch);
|
|
142
139
|
}
|
|
143
140
|
}
|
|
144
141
|
else if (!input.tags && !input.delete) {
|
|
145
142
|
// If no branch, tags, or delete specified, push the current branch by default
|
|
146
|
-
// Git might handle this automatically, but being explicit can be clearer
|
|
147
|
-
// command += ' HEAD'; // Or let git figure out the default push behavior
|
|
148
143
|
logger.debug("No specific branch, tags, or delete specified. Relying on default git push behavior for current branch.", { ...context, operation });
|
|
149
144
|
}
|
|
150
|
-
logger.debug(`Executing command: ${
|
|
145
|
+
logger.debug(`Executing command: git ${args.join(" ")}`, {
|
|
146
|
+
...context,
|
|
147
|
+
operation,
|
|
148
|
+
});
|
|
151
149
|
// Execute command. Note: Git push often uses stderr for progress and success messages.
|
|
152
|
-
const { stdout, stderr } = await
|
|
150
|
+
const { stdout, stderr } = await execFileAsync("git", args);
|
|
153
151
|
logger.debug(`Git push stdout: ${stdout}`, { ...context, operation });
|
|
154
152
|
if (stderr) {
|
|
155
153
|
logger.debug(`Git push stderr: ${stderr}`, { ...context, operation });
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { execFile } from "child_process";
|
|
2
2
|
import { promisify } from "util";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
// Import utils from barrel (logger from ../utils/internal/logger.js)
|
|
@@ -7,7 +7,7 @@ import { logger } from "../../../utils/index.js";
|
|
|
7
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
9
|
import { sanitization } from "../../../utils/index.js";
|
|
10
|
-
const
|
|
10
|
+
const execFileAsync = promisify(execFile);
|
|
11
11
|
// Define the BASE input schema for the git_rebase tool using Zod
|
|
12
12
|
export const GitRebaseBaseSchema = z.object({
|
|
13
13
|
path: z
|
|
@@ -117,39 +117,48 @@ export async function gitRebaseLogic(input, context) {
|
|
|
117
117
|
throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Invalid path: ${error instanceof Error ? error.message : String(error)}`, { context, operation, originalError: error });
|
|
118
118
|
}
|
|
119
119
|
try {
|
|
120
|
-
|
|
120
|
+
const args = ["-C", targetPath, "rebase"];
|
|
121
121
|
switch (input.mode) {
|
|
122
122
|
case "start":
|
|
123
|
-
if (input.interactive)
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
if (input.
|
|
130
|
-
|
|
123
|
+
if (input.interactive) {
|
|
124
|
+
args.push("-i");
|
|
125
|
+
}
|
|
126
|
+
if (input.strategy) {
|
|
127
|
+
args.push(`--strategy=${input.strategy}`);
|
|
128
|
+
}
|
|
129
|
+
if (input.strategyOption) {
|
|
130
|
+
args.push(`-X${input.strategyOption}`);
|
|
131
|
+
} // Note: -X for strategy options
|
|
132
|
+
if (input.onto) {
|
|
133
|
+
args.push("--onto", input.onto);
|
|
134
|
+
}
|
|
131
135
|
// Upstream is required by refine unless interactive
|
|
132
|
-
if (input.upstream)
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
+
if (input.upstream) {
|
|
137
|
+
args.push(input.upstream);
|
|
138
|
+
}
|
|
139
|
+
if (input.branch) {
|
|
140
|
+
args.push(input.branch);
|
|
141
|
+
}
|
|
136
142
|
break;
|
|
137
143
|
case "continue":
|
|
138
|
-
|
|
144
|
+
args.push("--continue");
|
|
139
145
|
break;
|
|
140
146
|
case "abort":
|
|
141
|
-
|
|
147
|
+
args.push("--abort");
|
|
142
148
|
break;
|
|
143
149
|
case "skip":
|
|
144
|
-
|
|
150
|
+
args.push("--skip");
|
|
145
151
|
break;
|
|
146
152
|
default:
|
|
147
153
|
// Should not happen due to Zod validation
|
|
148
154
|
throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Invalid mode: ${input.mode}`, { context, operation });
|
|
149
155
|
}
|
|
150
|
-
logger.debug(`Executing command: ${
|
|
156
|
+
logger.debug(`Executing command: git ${args.join(" ")}`, {
|
|
157
|
+
...context,
|
|
158
|
+
operation,
|
|
159
|
+
});
|
|
151
160
|
try {
|
|
152
|
-
const { stdout, stderr } = await
|
|
161
|
+
const { stdout, stderr } = await execFileAsync("git", args);
|
|
153
162
|
const output = stdout + stderr;
|
|
154
163
|
const message = `Rebase ${input.mode} executed successfully. Output: ${output.trim()}`;
|
|
155
164
|
logger.info(message, { ...context, operation, path: targetPath });
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { execFile } from "child_process";
|
|
2
2
|
import { promisify } from "util";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { BaseErrorCode, McpError } from "../../../types-global/errors.js"; // Direct import for types-global
|
|
5
5
|
import { logger, sanitization } from "../../../utils/index.js"; // Logger (./utils/internal/logger.js) & RequestContext (./utils/internal/requestContext.js) & sanitization (./utils/security/sanitization.js)
|
|
6
|
-
const
|
|
6
|
+
const execFileAsync = promisify(execFile);
|
|
7
7
|
// Define the input schema for the git_remote tool using Zod
|
|
8
8
|
export const GitRemoteInputSchema = z.object({
|
|
9
9
|
path: z
|
|
@@ -79,16 +79,16 @@ export async function gitRemoteLogic(input, context) {
|
|
|
79
79
|
throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Invalid path: ${error instanceof Error ? error.message : String(error)}`, { context, operation, originalError: error });
|
|
80
80
|
}
|
|
81
81
|
try {
|
|
82
|
-
let
|
|
82
|
+
let args;
|
|
83
83
|
let result;
|
|
84
84
|
switch (input.mode) {
|
|
85
85
|
case "list":
|
|
86
|
-
|
|
87
|
-
logger.debug(`Executing command: ${
|
|
86
|
+
args = ["-C", targetPath, "remote", "-v"];
|
|
87
|
+
logger.debug(`Executing command: git ${args.join(" ")}`, {
|
|
88
88
|
...context,
|
|
89
89
|
operation,
|
|
90
90
|
});
|
|
91
|
-
const { stdout: listStdout } = await
|
|
91
|
+
const { stdout: listStdout } = await execFileAsync("git", args);
|
|
92
92
|
const remotes = [];
|
|
93
93
|
const lines = listStdout.trim().split("\n");
|
|
94
94
|
const remoteMap = new Map();
|
|
@@ -127,12 +127,12 @@ export async function gitRemoteLogic(input, context) {
|
|
|
127
127
|
if (!/^[a-zA-Z0-9_.-]+$/.test(input.name)) {
|
|
128
128
|
throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Invalid remote name: ${input.name}`, { context, operation });
|
|
129
129
|
}
|
|
130
|
-
|
|
131
|
-
logger.debug(`Executing command: ${
|
|
130
|
+
args = ["-C", targetPath, "remote", "add", input.name, input.url];
|
|
131
|
+
logger.debug(`Executing command: git ${args.join(" ")}`, {
|
|
132
132
|
...context,
|
|
133
133
|
operation,
|
|
134
134
|
});
|
|
135
|
-
await
|
|
135
|
+
await execFileAsync("git", args);
|
|
136
136
|
result = {
|
|
137
137
|
success: true,
|
|
138
138
|
mode: "add",
|
|
@@ -146,12 +146,12 @@ export async function gitRemoteLogic(input, context) {
|
|
|
146
146
|
if (!/^[a-zA-Z0-9_.-]+$/.test(input.name)) {
|
|
147
147
|
throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Invalid remote name: ${input.name}`, { context, operation });
|
|
148
148
|
}
|
|
149
|
-
|
|
150
|
-
logger.debug(`Executing command: ${
|
|
149
|
+
args = ["-C", targetPath, "remote", "remove", input.name];
|
|
150
|
+
logger.debug(`Executing command: git ${args.join(" ")}`, {
|
|
151
151
|
...context,
|
|
152
152
|
operation,
|
|
153
153
|
});
|
|
154
|
-
await
|
|
154
|
+
await execFileAsync("git", args);
|
|
155
155
|
result = {
|
|
156
156
|
success: true,
|
|
157
157
|
mode: "remove",
|
|
@@ -165,12 +165,12 @@ export async function gitRemoteLogic(input, context) {
|
|
|
165
165
|
if (!/^[a-zA-Z0-9_.-]+$/.test(input.name)) {
|
|
166
166
|
throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Invalid remote name: ${input.name}`, { context, operation });
|
|
167
167
|
}
|
|
168
|
-
|
|
169
|
-
logger.debug(`Executing command: ${
|
|
168
|
+
args = ["-C", targetPath, "remote", "show", input.name];
|
|
169
|
+
logger.debug(`Executing command: git ${args.join(" ")}`, {
|
|
170
170
|
...context,
|
|
171
171
|
operation,
|
|
172
172
|
});
|
|
173
|
-
const { stdout: showStdout } = await
|
|
173
|
+
const { stdout: showStdout } = await execFileAsync("git", args);
|
|
174
174
|
result = { success: true, mode: "show", details: showStdout.trim() };
|
|
175
175
|
break;
|
|
176
176
|
default:
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { execFile } from "child_process";
|
|
2
2
|
import { promisify } from "util";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { BaseErrorCode, McpError } from "../../../types-global/errors.js"; // Direct import for types-global
|
|
5
5
|
import { logger, sanitization } from "../../../utils/index.js"; // logger (./utils/internal/logger.js), RequestContext (./utils/internal/requestContext.js), sanitization (./utils/security/sanitization.js)
|
|
6
|
-
const
|
|
6
|
+
const execFileAsync = promisify(execFile);
|
|
7
7
|
// Define the reset modes
|
|
8
8
|
const ResetModeEnum = z.enum(["soft", "mixed", "hard", "merge", "keep"]);
|
|
9
9
|
// Define the input schema for the git_reset tool using Zod
|
|
@@ -73,24 +73,25 @@ export async function resetGitState(input, context) {
|
|
|
73
73
|
throw error;
|
|
74
74
|
throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Invalid path: ${error instanceof Error ? error.message : String(error)}`, { context, operation, originalError: error });
|
|
75
75
|
}
|
|
76
|
-
// Basic sanitization for commit ref
|
|
77
|
-
const safeCommit = input.commit?.replace(/[`$&;*()|<>]/g, "");
|
|
78
76
|
try {
|
|
79
77
|
// Construct the git reset command
|
|
80
|
-
|
|
78
|
+
const args = ["-C", targetPath, "reset"];
|
|
81
79
|
if (input.mode) {
|
|
82
|
-
|
|
80
|
+
args.push(`--${input.mode}`);
|
|
83
81
|
}
|
|
84
|
-
if (
|
|
85
|
-
|
|
82
|
+
if (input.commit) {
|
|
83
|
+
args.push(input.commit);
|
|
86
84
|
}
|
|
87
85
|
// Handling file paths requires careful command construction, often without a commit ref.
|
|
88
86
|
// Example: `git reset HEAD -- path/to/file` or `git reset -- path/to/file` (unstages)
|
|
89
87
|
// For simplicity, this initial version focuses on resetting the whole HEAD/index/tree.
|
|
90
88
|
// Add file path logic here if needed, adjusting command structure.
|
|
91
|
-
logger.debug(`Executing command: ${
|
|
89
|
+
logger.debug(`Executing command: git ${args.join(" ")}`, {
|
|
90
|
+
...context,
|
|
91
|
+
operation,
|
|
92
|
+
});
|
|
92
93
|
// Execute command. Reset output is often minimal on success, but stderr might indicate issues.
|
|
93
|
-
const { stdout, stderr } = await
|
|
94
|
+
const { stdout, stderr } = await execFileAsync("git", args);
|
|
94
95
|
logger.debug(`Git reset stdout: ${stdout}`, { ...context, operation });
|
|
95
96
|
if (stderr) {
|
|
96
97
|
// Log stderr as info, as it often contains the primary status message
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { execFile } from "child_process";
|
|
2
2
|
import fs from "fs/promises";
|
|
3
3
|
import { promisify } from "util";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import { BaseErrorCode, McpError } from "../../../types-global/errors.js"; // Direct import for types-global
|
|
6
6
|
import { logger, sanitization } from "../../../utils/index.js"; // RequestContext (./utils/internal/requestContext.js), logger (./utils/internal/logger.js), sanitization (./utils/security/sanitization.js)
|
|
7
|
-
const
|
|
7
|
+
const execFileAsync = promisify(execFile);
|
|
8
8
|
// Define the Zod schema for input validation
|
|
9
9
|
export const GitSetWorkingDirInputSchema = z.object({
|
|
10
10
|
path: z
|
|
@@ -70,7 +70,7 @@ export async function gitSetWorkingDirLogic(input, context) {
|
|
|
70
70
|
let isGitRepo = false;
|
|
71
71
|
let initializedRepo = false;
|
|
72
72
|
try {
|
|
73
|
-
const { stdout } = await
|
|
73
|
+
const { stdout } = await execFileAsync("git", ["rev-parse", "--is-inside-work-tree"], {
|
|
74
74
|
cwd: sanitizedPath,
|
|
75
75
|
});
|
|
76
76
|
if (stdout.trim() === "true") {
|
|
@@ -94,7 +94,9 @@ export async function gitSetWorkingDirLogic(input, context) {
|
|
|
94
94
|
if (!isGitRepo && input.initializeIfNotPresent) {
|
|
95
95
|
logger.info(`Path is not a Git repository. Attempting to initialize (initializeIfNotPresent=true) with initial branch 'main'.`, { ...context, operation, path: sanitizedPath });
|
|
96
96
|
try {
|
|
97
|
-
await
|
|
97
|
+
await execFileAsync("git", ["init", "--initial-branch=main"], {
|
|
98
|
+
cwd: sanitizedPath,
|
|
99
|
+
});
|
|
98
100
|
initializedRepo = true;
|
|
99
101
|
isGitRepo = true; // Now it is a git repo
|
|
100
102
|
logger.info('Successfully initialized Git repository with initial branch "main".', { ...context, operation, path: sanitizedPath });
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { execFile } from "child_process";
|
|
2
2
|
import { promisify } from "util";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { BaseErrorCode, McpError } from "../../../types-global/errors.js"; // Direct import for types-global
|
|
5
5
|
import { logger, sanitization } from "../../../utils/index.js"; // logger (./utils/internal/logger.js), RequestContext (./utils/internal/requestContext.js), sanitization (./utils/security/sanitization.js)
|
|
6
|
-
const
|
|
6
|
+
const execFileAsync = promisify(execFile);
|
|
7
7
|
// Define the input schema for the git_show tool using Zod
|
|
8
8
|
// No refinements needed here, so we don't need a separate BaseSchema
|
|
9
9
|
export const GitShowInputSchema = z.object({
|
|
@@ -88,15 +88,16 @@ export async function gitShowLogic(input, context) {
|
|
|
88
88
|
}
|
|
89
89
|
try {
|
|
90
90
|
// Construct the refspec, combining ref and filePath if needed
|
|
91
|
-
const refSpec = input.filePath
|
|
92
|
-
? `${input.ref}:"${input.filePath}"`
|
|
93
|
-
: `"${input.ref}"`;
|
|
91
|
+
const refSpec = input.filePath ? `${input.ref}:${input.filePath}` : input.ref;
|
|
94
92
|
// Construct the command
|
|
95
|
-
const
|
|
96
|
-
logger.debug(`Executing command: ${
|
|
93
|
+
const args = ["-C", targetPath, "show", refSpec];
|
|
94
|
+
logger.debug(`Executing command: git ${args.join(" ")}`, {
|
|
95
|
+
...context,
|
|
96
|
+
operation,
|
|
97
|
+
});
|
|
97
98
|
// Execute command. Note: git show might write to stderr for non-error info (like commit details before diff)
|
|
98
99
|
// We primarily care about stdout for the content. Errors usually have non-zero exit code.
|
|
99
|
-
const { stdout, stderr } = await
|
|
100
|
+
const { stdout, stderr } = await execFileAsync("git", args);
|
|
100
101
|
if (stderr) {
|
|
101
102
|
// Log stderr as debug info, as it might contain commit details etc.
|
|
102
103
|
logger.debug(`Git show command produced stderr (may be informational)`, {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { execFile } from "child_process";
|
|
2
2
|
import { promisify } from "util";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { BaseErrorCode, McpError } from "../../../types-global/errors.js"; // Direct import for types-global
|
|
5
5
|
import { logger, sanitization } from "../../../utils/index.js"; // logger (./utils/internal/logger.js), RequestContext (./utils/internal/requestContext.js), sanitization (./utils/security/sanitization.js)
|
|
6
|
-
const
|
|
6
|
+
const execFileAsync = promisify(execFile);
|
|
7
7
|
// Define the BASE input schema for the git_stash tool using Zod
|
|
8
8
|
export const GitStashBaseSchema = z.object({
|
|
9
9
|
path: z
|
|
@@ -90,16 +90,16 @@ export async function gitStashLogic(input, context) {
|
|
|
90
90
|
throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Invalid stash reference format: ${input.stashRef}. Expected format: stash@{n}`, { context, operation });
|
|
91
91
|
}
|
|
92
92
|
try {
|
|
93
|
-
let
|
|
93
|
+
let args;
|
|
94
94
|
let result;
|
|
95
95
|
switch (input.mode) {
|
|
96
96
|
case "list":
|
|
97
|
-
|
|
98
|
-
logger.debug(`Executing command: ${
|
|
97
|
+
args = ["-C", targetPath, "stash", "list"];
|
|
98
|
+
logger.debug(`Executing command: git ${args.join(" ")}`, {
|
|
99
99
|
...context,
|
|
100
100
|
operation,
|
|
101
101
|
});
|
|
102
|
-
const { stdout: listStdout } = await
|
|
102
|
+
const { stdout: listStdout } = await execFileAsync("git", args);
|
|
103
103
|
const stashes = listStdout
|
|
104
104
|
.trim()
|
|
105
105
|
.split("\n")
|
|
@@ -121,13 +121,13 @@ export async function gitStashLogic(input, context) {
|
|
|
121
121
|
case "pop":
|
|
122
122
|
// stashRef is validated by Zod refine
|
|
123
123
|
const stashRefApplyPop = input.stashRef;
|
|
124
|
-
|
|
125
|
-
logger.debug(`Executing command: ${
|
|
124
|
+
args = ["-C", targetPath, "stash", input.mode, stashRefApplyPop];
|
|
125
|
+
logger.debug(`Executing command: git ${args.join(" ")}`, {
|
|
126
126
|
...context,
|
|
127
127
|
operation,
|
|
128
128
|
});
|
|
129
129
|
try {
|
|
130
|
-
const { stdout, stderr } = await
|
|
130
|
+
const { stdout, stderr } = await execFileAsync("git", args);
|
|
131
131
|
// Check stdout/stderr for conflict messages, although exit code 0 usually means success
|
|
132
132
|
const conflicts = /conflict/i.test(stdout) || /conflict/i.test(stderr);
|
|
133
133
|
const message = conflicts
|
|
@@ -166,12 +166,12 @@ export async function gitStashLogic(input, context) {
|
|
|
166
166
|
case "drop":
|
|
167
167
|
// stashRef is validated by Zod refine
|
|
168
168
|
const stashRefDrop = input.stashRef;
|
|
169
|
-
|
|
170
|
-
logger.debug(`Executing command: ${
|
|
169
|
+
args = ["-C", targetPath, "stash", "drop", stashRefDrop];
|
|
170
|
+
logger.debug(`Executing command: git ${args.join(" ")}`, {
|
|
171
171
|
...context,
|
|
172
172
|
operation,
|
|
173
173
|
});
|
|
174
|
-
await
|
|
174
|
+
await execFileAsync("git", args);
|
|
175
175
|
result = {
|
|
176
176
|
success: true,
|
|
177
177
|
mode: "drop",
|
|
@@ -180,16 +180,15 @@ export async function gitStashLogic(input, context) {
|
|
|
180
180
|
};
|
|
181
181
|
break;
|
|
182
182
|
case "save":
|
|
183
|
-
|
|
183
|
+
args = ["-C", targetPath, "stash", "save"];
|
|
184
184
|
if (input.message) {
|
|
185
|
-
|
|
186
|
-
command += ` "${input.message.replace(/"/g, '\\"')}"`;
|
|
185
|
+
args.push(input.message);
|
|
187
186
|
}
|
|
188
|
-
logger.debug(`Executing command: ${
|
|
187
|
+
logger.debug(`Executing command: git ${args.join(" ")}`, {
|
|
189
188
|
...context,
|
|
190
189
|
operation,
|
|
191
190
|
});
|
|
192
|
-
const { stdout: saveStdout } = await
|
|
191
|
+
const { stdout: saveStdout } = await execFileAsync("git", args);
|
|
193
192
|
const stashCreated = !/no local changes to save/i.test(saveStdout);
|
|
194
193
|
const saveMessage = stashCreated
|
|
195
194
|
? `Changes stashed successfully.` +
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { execFile } from "child_process";
|
|
2
2
|
import { promisify } from "util";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { BaseErrorCode, McpError } from "../../../types-global/errors.js"; // Direct import for types-global
|
|
5
5
|
import { logger, sanitization } from "../../../utils/index.js"; // logger (./utils/internal/logger.js), RequestContext (./utils/internal/requestContext.js), sanitization (./utils/security/sanitization.js)
|
|
6
|
-
const
|
|
6
|
+
const execFileAsync = promisify(execFile);
|
|
7
7
|
// Define the input schema for the git_status tool using Zod
|
|
8
8
|
export const GitStatusInputSchema = z.object({
|
|
9
9
|
path: z
|
|
@@ -197,10 +197,12 @@ export async function getGitStatus(input, context) {
|
|
|
197
197
|
}
|
|
198
198
|
try {
|
|
199
199
|
// Using --porcelain=v1 for stable, scriptable output and -b for branch info
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
200
|
+
const args = ["-C", targetPath, "status", "--porcelain=v1", "-b"];
|
|
201
|
+
logger.debug(`Executing command: git ${args.join(" ")}`, {
|
|
202
|
+
...context,
|
|
203
|
+
operation,
|
|
204
|
+
});
|
|
205
|
+
const { stdout, stderr } = await execFileAsync("git", args);
|
|
204
206
|
if (stderr) {
|
|
205
207
|
// Log stderr as warning but proceed to parse stdout
|
|
206
208
|
logger.warning(`Git status command produced stderr (may be informational)`, { ...context, operation, stderr });
|
|
@@ -216,8 +218,8 @@ export async function getGitStatus(input, context) {
|
|
|
216
218
|
// This handles the case of an empty repo after init but before first commit
|
|
217
219
|
if (structuredResult.is_clean && !structuredResult.current_branch) {
|
|
218
220
|
try {
|
|
219
|
-
const
|
|
220
|
-
const { stdout: branchStdout } = await
|
|
221
|
+
const branchArgs = ["-C", targetPath, "rev-parse", "--abbrev-ref", "HEAD"];
|
|
222
|
+
const { stdout: branchStdout } = await execFileAsync("git", branchArgs);
|
|
221
223
|
const currentBranchName = branchStdout.trim(); // Renamed variable for clarity
|
|
222
224
|
if (currentBranchName && currentBranchName !== "HEAD") {
|
|
223
225
|
structuredResult.current_branch = currentBranchName;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { execFile } from "child_process";
|
|
2
2
|
import { promisify } from "util";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { BaseErrorCode, McpError } from "../../../types-global/errors.js"; // Direct import for types-global
|
|
5
5
|
import { logger, sanitization } from "../../../utils/index.js"; // logger (./utils/internal/logger.js), RequestContext (./utils/internal/requestContext.js), sanitization (./utils/security/sanitization.js)
|
|
6
|
-
const
|
|
6
|
+
const execFileAsync = promisify(execFile);
|
|
7
7
|
// Define the base input schema for the git_tag tool using Zod
|
|
8
8
|
// We export this separately to access its .shape for registration
|
|
9
9
|
export const GitTagBaseSchema = z.object({
|
|
@@ -111,16 +111,16 @@ export async function gitTagLogic(input, context) {
|
|
|
111
111
|
throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Invalid commit reference format: ${input.commitRef}`, { context, operation });
|
|
112
112
|
}
|
|
113
113
|
try {
|
|
114
|
-
let
|
|
114
|
+
let args;
|
|
115
115
|
let result;
|
|
116
116
|
switch (input.mode) {
|
|
117
117
|
case "list":
|
|
118
|
-
|
|
119
|
-
logger.debug(`Executing command: ${
|
|
118
|
+
args = ["-C", targetPath, "tag", "--list"];
|
|
119
|
+
logger.debug(`Executing command: git ${args.join(" ")}`, {
|
|
120
120
|
...context,
|
|
121
121
|
operation,
|
|
122
122
|
});
|
|
123
|
-
const { stdout: listStdout } = await
|
|
123
|
+
const { stdout: listStdout } = await execFileAsync("git", args);
|
|
124
124
|
const tags = listStdout
|
|
125
125
|
.trim()
|
|
126
126
|
.split("\n")
|
|
@@ -130,20 +130,20 @@ export async function gitTagLogic(input, context) {
|
|
|
130
130
|
case "create":
|
|
131
131
|
// TagName is validated by Zod refine
|
|
132
132
|
const tagNameCreate = input.tagName;
|
|
133
|
-
|
|
133
|
+
args = ["-C", targetPath, "tag"];
|
|
134
134
|
if (input.annotate) {
|
|
135
135
|
// Message is validated by Zod refine
|
|
136
|
-
|
|
136
|
+
args.push("-a", "-m", input.message);
|
|
137
137
|
}
|
|
138
|
-
|
|
138
|
+
args.push(tagNameCreate);
|
|
139
139
|
if (input.commitRef) {
|
|
140
|
-
|
|
140
|
+
args.push(input.commitRef);
|
|
141
141
|
}
|
|
142
|
-
logger.debug(`Executing command: ${
|
|
142
|
+
logger.debug(`Executing command: git ${args.join(" ")}`, {
|
|
143
143
|
...context,
|
|
144
144
|
operation,
|
|
145
145
|
});
|
|
146
|
-
await
|
|
146
|
+
await execFileAsync("git", args);
|
|
147
147
|
result = {
|
|
148
148
|
success: true,
|
|
149
149
|
mode: "create",
|
|
@@ -154,12 +154,12 @@ export async function gitTagLogic(input, context) {
|
|
|
154
154
|
case "delete":
|
|
155
155
|
// TagName is validated by Zod refine
|
|
156
156
|
const tagNameDelete = input.tagName;
|
|
157
|
-
|
|
158
|
-
logger.debug(`Executing command: ${
|
|
157
|
+
args = ["-C", targetPath, "tag", "-d", tagNameDelete];
|
|
158
|
+
logger.debug(`Executing command: git ${args.join(" ")}`, {
|
|
159
159
|
...context,
|
|
160
160
|
operation,
|
|
161
161
|
});
|
|
162
|
-
await
|
|
162
|
+
await execFileAsync("git", args);
|
|
163
163
|
result = {
|
|
164
164
|
success: true,
|
|
165
165
|
mode: "delete",
|