@cyanheads/git-mcp-server 2.10.4 → 2.10.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 +1 -1
- package/dist/index.js +67 -37
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
<div align="center">
|
|
9
9
|
|
|
10
|
-
[](./CHANGELOG.md) [](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/specification/2025-11-25/changelog.mdx) [](https://modelcontextprotocol.io/) [](./LICENSE) [](https://github.com/cyanheads/git-mcp-server/issues) [](https://www.typescriptlang.org/) [](https://bun.sh/)
|
|
11
11
|
|
|
12
12
|
</div>
|
|
13
13
|
|
package/dist/index.js
CHANGED
|
@@ -15357,7 +15357,7 @@ var package_default;
|
|
|
15357
15357
|
var init_package = __esm(() => {
|
|
15358
15358
|
package_default = {
|
|
15359
15359
|
name: "@cyanheads/git-mcp-server",
|
|
15360
|
-
version: "2.10.
|
|
15360
|
+
version: "2.10.5",
|
|
15361
15361
|
mcpName: "io.github.cyanheads/git-mcp-server",
|
|
15362
15362
|
description: "A secure and scalable Git MCP server enabling AI agents to perform comprehensive Git version control operations via STDIO and Streamable HTTP.",
|
|
15363
15363
|
main: "dist/index.js",
|
|
@@ -136968,6 +136968,9 @@ class BaseGitProvider {
|
|
|
136968
136968
|
}
|
|
136969
136969
|
}
|
|
136970
136970
|
|
|
136971
|
+
// src/services/git/providers/cli/operations/core/init.ts
|
|
136972
|
+
import { mkdir } from "node:fs/promises";
|
|
136973
|
+
|
|
136971
136974
|
// src/services/git/providers/cli/utils/command-builder.ts
|
|
136972
136975
|
function buildGitCommand(config3) {
|
|
136973
136976
|
const parts = [config3.command];
|
|
@@ -137567,9 +137570,9 @@ async function executeInit(options, context, execGit) {
|
|
|
137567
137570
|
}
|
|
137568
137571
|
const initialBranch = options.initialBranch || "main";
|
|
137569
137572
|
args.push(`--initial-branch=${initialBranch}`);
|
|
137570
|
-
|
|
137573
|
+
await mkdir(options.path, { recursive: true }).catch(() => {});
|
|
137571
137574
|
const cmd = buildGitCommand({ command: "init", args });
|
|
137572
|
-
await execGit(cmd,
|
|
137575
|
+
await execGit(cmd, options.path, context.requestContext);
|
|
137573
137576
|
const result = {
|
|
137574
137577
|
success: true,
|
|
137575
137578
|
path: options.path,
|
|
@@ -138009,8 +138012,10 @@ async function executeDiff(options, context, execGit) {
|
|
|
138009
138012
|
});
|
|
138010
138013
|
const statResult2 = await execGit(statCmd2, context.workingDirectory, context.requestContext);
|
|
138011
138014
|
const stats2 = parseGitDiffStat(statResult2.stdout);
|
|
138012
|
-
let untrackedStatOutput2 = "";
|
|
138013
138015
|
let untrackedFileCount2 = 0;
|
|
138016
|
+
let untrackedAdditions2 = 0;
|
|
138017
|
+
let untrackedDeletions2 = 0;
|
|
138018
|
+
let untrackedStatOutput = "";
|
|
138014
138019
|
if (options.includeUntracked) {
|
|
138015
138020
|
let untrackedFiles = await getUntrackedFiles(execGit, context);
|
|
138016
138021
|
if (options.excludePatterns?.length) {
|
|
@@ -138019,18 +138024,20 @@ async function executeDiff(options, context, execGit) {
|
|
|
138019
138024
|
for (const file2 of untrackedFiles) {
|
|
138020
138025
|
const result = await execUntrackedDiff(execGit, context, file2, true);
|
|
138021
138026
|
if (result) {
|
|
138022
|
-
|
|
138027
|
+
const fileStat = parseGitDiffStat(result);
|
|
138028
|
+
untrackedAdditions2 += fileStat.totalAdditions;
|
|
138029
|
+
untrackedDeletions2 += fileStat.totalDeletions;
|
|
138030
|
+
untrackedStatOutput += result;
|
|
138023
138031
|
untrackedFileCount2++;
|
|
138024
138032
|
}
|
|
138025
138033
|
}
|
|
138026
138034
|
}
|
|
138027
|
-
const untrackedStats2 = untrackedStatOutput2 ? parseGitDiffStat(untrackedStatOutput2) : { totalAdditions: 0, totalDeletions: 0 };
|
|
138028
138035
|
return {
|
|
138029
|
-
diff: statResult2.stdout +
|
|
138036
|
+
diff: statResult2.stdout + untrackedStatOutput,
|
|
138030
138037
|
filesChanged: stats2.files.length + untrackedFileCount2,
|
|
138031
|
-
insertions: stats2.totalAdditions +
|
|
138032
|
-
deletions: stats2.totalDeletions +
|
|
138033
|
-
binary: statResult2.stdout.includes("Binary files") ||
|
|
138038
|
+
insertions: stats2.totalAdditions + untrackedAdditions2,
|
|
138039
|
+
deletions: stats2.totalDeletions + untrackedDeletions2,
|
|
138040
|
+
binary: statResult2.stdout.includes("Binary files") || untrackedStatOutput.includes("Binary files"),
|
|
138034
138041
|
...excludedFiles && { excludedFiles }
|
|
138035
138042
|
};
|
|
138036
138043
|
}
|
|
@@ -138039,7 +138046,8 @@ async function executeDiff(options, context, execGit) {
|
|
|
138039
138046
|
const diffResult = await execGit(diffCmd, context.workingDirectory, context.requestContext);
|
|
138040
138047
|
let untrackedDiff = "";
|
|
138041
138048
|
let untrackedFileCount = 0;
|
|
138042
|
-
let
|
|
138049
|
+
let untrackedAdditions = 0;
|
|
138050
|
+
let untrackedDeletions = 0;
|
|
138043
138051
|
if (options.includeUntracked) {
|
|
138044
138052
|
let untrackedFiles = await getUntrackedFiles(execGit, context);
|
|
138045
138053
|
if (options.excludePatterns?.length) {
|
|
@@ -138057,7 +138065,9 @@ async function executeDiff(options, context, execGit) {
|
|
|
138057
138065
|
}
|
|
138058
138066
|
const statResult2 = await execUntrackedDiff(execGit, context, file2, true);
|
|
138059
138067
|
if (statResult2) {
|
|
138060
|
-
|
|
138068
|
+
const fileStat = parseGitDiffStat(statResult2);
|
|
138069
|
+
untrackedAdditions += fileStat.totalAdditions;
|
|
138070
|
+
untrackedDeletions += fileStat.totalDeletions;
|
|
138061
138071
|
}
|
|
138062
138072
|
}
|
|
138063
138073
|
}
|
|
@@ -138079,13 +138089,12 @@ async function executeDiff(options, context, execGit) {
|
|
|
138079
138089
|
});
|
|
138080
138090
|
const statResult = await execGit(statCmd, context.workingDirectory, context.requestContext);
|
|
138081
138091
|
const stats = parseGitDiffStat(statResult.stdout);
|
|
138082
|
-
const untrackedStats = untrackedStatOutput ? parseGitDiffStat(untrackedStatOutput) : { totalAdditions: 0, totalDeletions: 0 };
|
|
138083
138092
|
const hasBinary = combinedDiff.includes("Binary files");
|
|
138084
138093
|
return {
|
|
138085
138094
|
diff: combinedDiff,
|
|
138086
138095
|
filesChanged: stats.files.length + untrackedFileCount,
|
|
138087
|
-
insertions: stats.totalAdditions +
|
|
138088
|
-
deletions: stats.totalDeletions +
|
|
138096
|
+
insertions: stats.totalAdditions + untrackedAdditions,
|
|
138097
|
+
deletions: stats.totalDeletions + untrackedDeletions,
|
|
138089
138098
|
binary: hasBinary,
|
|
138090
138099
|
...excludedFiles && { excludedFiles }
|
|
138091
138100
|
};
|
|
@@ -138147,11 +138156,11 @@ async function executeBranch(options, context, execGit) {
|
|
|
138147
138156
|
].join(GIT_FIELD_DELIMITER);
|
|
138148
138157
|
const refPrefixes = options.all ? ["refs/heads", "refs/remotes"] : [options.remote ? "refs/remotes" : "refs/heads"];
|
|
138149
138158
|
args.push(`--format=${format}`, ...refPrefixes);
|
|
138150
|
-
if (options.merged !== undefined) {
|
|
138159
|
+
if (options.merged !== undefined && options.merged !== false) {
|
|
138151
138160
|
const mergedRef = typeof options.merged === "string" ? options.merged : "HEAD";
|
|
138152
138161
|
args.push(`--merged=${mergedRef}`);
|
|
138153
138162
|
}
|
|
138154
|
-
if (options.noMerged !== undefined) {
|
|
138163
|
+
if (options.noMerged !== undefined && options.noMerged !== false) {
|
|
138155
138164
|
const noMergedRef = typeof options.noMerged === "string" ? options.noMerged : "HEAD";
|
|
138156
138165
|
args.push(`--no-merged=${noMergedRef}`);
|
|
138157
138166
|
}
|
|
@@ -138239,7 +138248,7 @@ async function executeCheckout(options, context, execGit) {
|
|
|
138239
138248
|
const cmd = buildGitCommand({ command: "checkout", args });
|
|
138240
138249
|
const result = await execGit(cmd, context.workingDirectory, context.requestContext);
|
|
138241
138250
|
const filesModified = result.stdout.split(`
|
|
138242
|
-
`).
|
|
138251
|
+
`).map((line) => line.trim()).filter((line) => line && !line.startsWith("Switched") && !line.startsWith("Already") && !line.startsWith("Your branch") && !line.startsWith("(use ") && !line.startsWith("HEAD is now"));
|
|
138243
138252
|
const checkoutResult = {
|
|
138244
138253
|
success: true,
|
|
138245
138254
|
target: options.target,
|
|
@@ -138370,9 +138379,19 @@ async function executeRebase(options, context, execGit) {
|
|
|
138370
138379
|
const match = line.match(/CONFLICT.*?in (.+)$/);
|
|
138371
138380
|
return match?.[1] || "";
|
|
138372
138381
|
}).filter((f3) => f3);
|
|
138373
|
-
|
|
138374
|
-
|
|
138375
|
-
|
|
138382
|
+
let rebasedCommits = 0;
|
|
138383
|
+
const progressLines = result.stderr.split(`
|
|
138384
|
+
`).filter((line) => /Rebasing \(\d+\/\d+\)/.test(line));
|
|
138385
|
+
if (progressLines.length > 0) {
|
|
138386
|
+
const lastProgress = progressLines.at(-1);
|
|
138387
|
+
const progressMatch = lastProgress.match(/Rebasing \((\d+)\/(\d+)\)/);
|
|
138388
|
+
if (progressMatch) {
|
|
138389
|
+
rebasedCommits = parseInt(progressMatch[2], 10);
|
|
138390
|
+
}
|
|
138391
|
+
} else {
|
|
138392
|
+
rebasedCommits = result.stdout.split(`
|
|
138393
|
+
`).filter((line) => line.startsWith("Applying:")).length;
|
|
138394
|
+
}
|
|
138376
138395
|
const rebaseResult = {
|
|
138377
138396
|
success: !hasConflicts,
|
|
138378
138397
|
conflicts: hasConflicts,
|
|
@@ -138678,7 +138697,7 @@ async function executePull(options, context, execGit) {
|
|
|
138678
138697
|
}
|
|
138679
138698
|
const hasConflicts = result.stdout.includes("CONFLICT") || result.stderr.includes("CONFLICT");
|
|
138680
138699
|
const filesChanged = result.stdout.split(`
|
|
138681
|
-
`).
|
|
138700
|
+
`).map((line) => line.trim()).filter((line) => line && !line.includes("CONFLICT") && !line.startsWith("Already up to date") && !line.startsWith("From ") && !line.startsWith("Your branch") && !line.startsWith("(use ") && !line.startsWith("Updating ") && !line.startsWith("Fast-forward") && !line.match(/^\d+ files? changed/));
|
|
138682
138701
|
const pullResult = {
|
|
138683
138702
|
success: !hasConflicts,
|
|
138684
138703
|
remote,
|
|
@@ -138702,7 +138721,7 @@ async function executeTag(options, context, execGit) {
|
|
|
138702
138721
|
"%(refname:short)",
|
|
138703
138722
|
"%(if)%(*objectname:short)%(then)%(*objectname:short)%(else)%(objectname:short)%(end)",
|
|
138704
138723
|
"%(if)%(contents:subject)%(then)%(contents:subject)%(end)",
|
|
138705
|
-
"%(if)%(taggername)%(then)%(taggername)
|
|
138724
|
+
"%(if)%(taggername)%(then)%(taggername) %(taggeremail)%(end)",
|
|
138706
138725
|
"%(if)%(creatordate:unix)%(then)%(creatordate:unix)%(end)"
|
|
138707
138726
|
].join(GIT_FIELD_DELIMITER);
|
|
138708
138727
|
const refCmd = buildGitCommand({
|
|
@@ -138753,10 +138772,14 @@ async function executeTag(options, context, execGit) {
|
|
|
138753
138772
|
}
|
|
138754
138773
|
return createArgs;
|
|
138755
138774
|
};
|
|
138756
|
-
const
|
|
138757
|
-
|
|
138758
|
-
|
|
138759
|
-
|
|
138775
|
+
const configOverride = shouldSign ? [] : ["-c", "tag.gpgSign=false"];
|
|
138776
|
+
const createCmd = [
|
|
138777
|
+
...configOverride,
|
|
138778
|
+
...buildGitCommand({
|
|
138779
|
+
command: "tag",
|
|
138780
|
+
args: buildCreateArgs(shouldSign)
|
|
138781
|
+
})
|
|
138782
|
+
];
|
|
138760
138783
|
try {
|
|
138761
138784
|
await execGit(createCmd, context.workingDirectory, context.requestContext);
|
|
138762
138785
|
} catch (error48) {
|
|
@@ -138810,7 +138833,8 @@ async function executeStash(options, context, execGit) {
|
|
|
138810
138833
|
const parts = line.split("\t");
|
|
138811
138834
|
const [refPart, tsPart, ...subjectParts] = parts;
|
|
138812
138835
|
if (refPart && tsPart && subjectParts.length > 0) {
|
|
138813
|
-
const
|
|
138836
|
+
const indexMatch = refPart.match(/\{(\d+)\}/);
|
|
138837
|
+
const stashIndex = indexMatch ? parseInt(indexMatch[1], 10) : index;
|
|
138814
138838
|
const timestamp = parseInt(tsPart, 10);
|
|
138815
138839
|
const subject = subjectParts.join("\t");
|
|
138816
138840
|
const branchMatch = subject.match(/^(?:WIP on|On)\s+([^:]+):\s+(.*)$/);
|
|
@@ -149966,12 +149990,13 @@ var TOOL_TITLE3 = "Git Clean";
|
|
|
149966
149990
|
var TOOL_DESCRIPTION3 = "Remove untracked files from the working directory. Requires force flag for safety. Use dry-run to preview files that would be removed.";
|
|
149967
149991
|
var InputSchema3 = exports_external.object({
|
|
149968
149992
|
path: PathSchema,
|
|
149969
|
-
force: ForceSchema
|
|
149970
|
-
message: "force flag must be set to true to clean untracked files"
|
|
149971
|
-
}),
|
|
149993
|
+
force: ForceSchema,
|
|
149972
149994
|
dryRun: DryRunSchema,
|
|
149973
149995
|
directories: exports_external.boolean().default(false).describe("Remove untracked directories in addition to files."),
|
|
149974
149996
|
ignored: exports_external.boolean().default(false).describe("Remove ignored files as well.")
|
|
149997
|
+
}).refine((data) => data.force === true || data.dryRun === true, {
|
|
149998
|
+
message: "force flag must be set to true to clean untracked files (or use dryRun to preview)",
|
|
149999
|
+
path: ["force"]
|
|
149975
150000
|
});
|
|
149976
150001
|
var OutputSchema4 = exports_external.object({
|
|
149977
150002
|
success: exports_external.boolean().describe("Indicates if the operation was successful."),
|
|
@@ -150456,7 +150481,7 @@ var InputSchema9 = exports_external.object({
|
|
|
150456
150481
|
var OutputSchema10 = exports_external.object({
|
|
150457
150482
|
success: exports_external.boolean().describe("Indicates if the operation was successful."),
|
|
150458
150483
|
currentBranch: exports_external.string().nullable().describe("Current branch name."),
|
|
150459
|
-
isClean: exports_external.boolean().describe("True if working directory is clean."),
|
|
150484
|
+
isClean: exports_external.boolean().describe("True if working directory is clean (no staged, unstaged, or untracked changes). When includeUntracked is false, untracked files are excluded from this check."),
|
|
150460
150485
|
stagedChanges: exports_external.object({
|
|
150461
150486
|
added: exports_external.array(exports_external.string()).optional().describe("Files added to the index (staged)."),
|
|
150462
150487
|
modified: exports_external.array(exports_external.string()).optional().describe("Files modified and staged."),
|
|
@@ -150730,10 +150755,15 @@ async function gitAddLogic(input, { provider, targetPath, appContext }) {
|
|
|
150730
150755
|
requestContext: appContext,
|
|
150731
150756
|
tenantId: appContext.tenantId || "default-tenant"
|
|
150732
150757
|
});
|
|
150758
|
+
const stagedFiles = result.stagedFiles.length > 0 ? result.stagedFiles : [
|
|
150759
|
+
...statusResult.stagedChanges.added || [],
|
|
150760
|
+
...statusResult.stagedChanges.modified || [],
|
|
150761
|
+
...statusResult.stagedChanges.deleted || []
|
|
150762
|
+
];
|
|
150733
150763
|
return {
|
|
150734
150764
|
success: result.success,
|
|
150735
|
-
stagedFiles
|
|
150736
|
-
totalFiles:
|
|
150765
|
+
stagedFiles,
|
|
150766
|
+
totalFiles: stagedFiles.length,
|
|
150737
150767
|
status: {
|
|
150738
150768
|
current_branch: statusResult.currentBranch,
|
|
150739
150769
|
staged_changes: flattenChanges(statusResult.stagedChanges),
|
|
@@ -151130,7 +151160,7 @@ var TOOL_DESCRIPTION15 = "Show details of a git object (commit, tree, blob, or t
|
|
|
151130
151160
|
var InputSchema15 = exports_external.object({
|
|
151131
151161
|
path: PathSchema,
|
|
151132
151162
|
object: CommitRefSchema.describe("Git object to show (commit hash, branch, tag, tree, or blob)."),
|
|
151133
|
-
format: exports_external.enum(["raw"
|
|
151163
|
+
format: exports_external.enum(["raw"]).optional().describe('Output format for the git object. Use "raw" for unprocessed git output.'),
|
|
151134
151164
|
stat: exports_external.boolean().default(false).describe("Show diffstat instead of full diff."),
|
|
151135
151165
|
filePath: exports_external.string().optional().describe("View specific file at a given commit reference. When provided, shows the file content from the specified object.")
|
|
151136
151166
|
});
|
|
@@ -151213,8 +151243,8 @@ var InputSchema16 = exports_external.object({
|
|
|
151213
151243
|
force: ForceSchema,
|
|
151214
151244
|
all: AllSchema.describe("For list operation: show both local and remote branches."),
|
|
151215
151245
|
remote: exports_external.boolean().default(false).describe("For list operation: show only remote branches."),
|
|
151216
|
-
merged: exports_external.union([exports_external.boolean(), CommitRefSchema]).optional().describe("For list operation: show only branches merged into HEAD (true) or specified commit (string)."),
|
|
151217
|
-
noMerged: exports_external.union([exports_external.boolean(), CommitRefSchema]).optional().describe("For list operation: show only branches not merged into HEAD (true) or specified commit (string).")
|
|
151246
|
+
merged: exports_external.preprocess((val) => val === "true" ? true : val === "false" ? false : val, exports_external.union([exports_external.boolean(), CommitRefSchema])).optional().describe("For list operation: show only branches merged into HEAD (true) or specified commit (string)."),
|
|
151247
|
+
noMerged: exports_external.preprocess((val) => val === "true" ? true : val === "false" ? false : val, exports_external.union([exports_external.boolean(), CommitRefSchema])).optional().describe("For list operation: show only branches not merged into HEAD (true) or specified commit (string).")
|
|
151218
151248
|
});
|
|
151219
151249
|
var BranchInfoSchema = exports_external.object({
|
|
151220
151250
|
name: exports_external.string().describe("Branch name."),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cyanheads/git-mcp-server",
|
|
3
|
-
"version": "2.10.
|
|
3
|
+
"version": "2.10.5",
|
|
4
4
|
"mcpName": "io.github.cyanheads/git-mcp-server",
|
|
5
5
|
"description": "A secure and scalable Git MCP server enabling AI agents to perform comprehensive Git version control operations via STDIO and Streamable HTTP.",
|
|
6
6
|
"main": "dist/index.js",
|