@cyanheads/git-mcp-server 2.14.0 → 2.14.2
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 -10
- package/dist/index.js +148 -10
- 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
|
|
|
@@ -17,15 +17,15 @@
|
|
|
17
17
|
|
|
18
18
|
28 git operations organized into seven categories:
|
|
19
19
|
|
|
20
|
-
| Category | Tools | Description
|
|
21
|
-
| :------------------------ | :----------------------------------------------------------------------------------------------------------------------------- |
|
|
22
|
-
| **Repository Management** | `git_init`, `git_clone`, `git_status`, `git_clean` | Initialize repos, clone from remotes, check status, clean untracked files
|
|
23
|
-
| **Staging & Commits** | `git_add`, `git_commit`, `git_diff` | Stage changes, create commits, compare changes
|
|
24
|
-
| **History & Inspection** | `git_log`, `git_show`, `git_blame`, `git_reflog` | View commit history, inspect objects, trace authorship, view ref logs
|
|
25
|
-
| **Analysis** | `git_changelog_analyze` | Gather git context and instructions for LLM-driven changelog analysis
|
|
26
|
-
| **Branching & Merging** | `git_branch`, `git_checkout`, `git_merge`, `git_rebase`, `git_cherry_pick` | Manage branches, switch contexts, integrate changes, apply specific commits
|
|
27
|
-
| **Remote Operations** | `git_remote`, `git_fetch`, `git_pull`, `git_push` | Configure remotes, fetch updates, synchronize repositories, publish changes
|
|
28
|
-
| **Advanced Workflows** | `git_tag`, `git_stash`, `git_reset`, `git_worktree`, `git_set_working_dir`, `git_clear_working_dir`, `git_wrapup_instructions` | Tag releases, stash changes, reset state, manage worktrees, set/clear session directory |
|
|
20
|
+
| Category | Tools | Description |
|
|
21
|
+
| :------------------------ | :----------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------ |
|
|
22
|
+
| **Repository Management** | `git_init`, `git_clone`, `git_status`, `git_clean` | Initialize repos, clone from remotes, check status, clean untracked files |
|
|
23
|
+
| **Staging & Commits** | `git_add`, `git_commit`, `git_diff` | Stage changes, create commits, compare changes |
|
|
24
|
+
| **History & Inspection** | `git_log`, `git_show`, `git_blame`, `git_reflog` | View commit history, inspect objects, trace authorship, view ref logs |
|
|
25
|
+
| **Analysis** | `git_changelog_analyze` | Gather git context and instructions for LLM-driven changelog analysis |
|
|
26
|
+
| **Branching & Merging** | `git_branch`, `git_checkout`, `git_merge`, `git_rebase`, `git_cherry_pick` | Manage branches, switch contexts, integrate changes, apply specific commits |
|
|
27
|
+
| **Remote Operations** | `git_remote`, `git_fetch`, `git_pull`, `git_push` | Configure remotes, fetch updates, synchronize repositories, publish changes |
|
|
28
|
+
| **Advanced Workflows** | `git_tag`, `git_stash`, `git_reset`, `git_worktree`, `git_set_working_dir`, `git_clear_working_dir`, `git_wrapup_instructions` | Tag releases (list/create/delete/verify), stash changes, reset state, manage worktrees, set/clear session directory |
|
|
29
29
|
|
|
30
30
|
## Resources
|
|
31
31
|
|
package/dist/index.js
CHANGED
|
@@ -15284,7 +15284,7 @@ var package_default;
|
|
|
15284
15284
|
var init_package = __esm(() => {
|
|
15285
15285
|
package_default = {
|
|
15286
15286
|
name: "@cyanheads/git-mcp-server",
|
|
15287
|
-
version: "2.
|
|
15287
|
+
version: "2.14.2",
|
|
15288
15288
|
mcpName: "io.github.cyanheads/git-mcp-server",
|
|
15289
15289
|
description: "A secure and scalable Git MCP server enabling AI agents to perform comprehensive Git version control operations via STDIO and Streamable HTTP.",
|
|
15290
15290
|
main: "dist/index.js",
|
|
@@ -134454,6 +134454,7 @@ function parseFilesChanged(stdout) {
|
|
|
134454
134454
|
return files;
|
|
134455
134455
|
}
|
|
134456
134456
|
// src/services/git/providers/cli/operations/tags/tag.ts
|
|
134457
|
+
init_errors3();
|
|
134457
134458
|
async function executeTag(options, context, execGit) {
|
|
134458
134459
|
try {
|
|
134459
134460
|
const args = [];
|
|
@@ -134572,6 +134573,17 @@ async function executeTag(options, context, execGit) {
|
|
|
134572
134573
|
};
|
|
134573
134574
|
return deleteResult;
|
|
134574
134575
|
}
|
|
134576
|
+
case "verify": {
|
|
134577
|
+
if (!options.tagName) {
|
|
134578
|
+
throw new Error("Tag name is required for verify operation");
|
|
134579
|
+
}
|
|
134580
|
+
const cmd = buildGitCommand({
|
|
134581
|
+
command: "tag",
|
|
134582
|
+
args: ["-v", options.tagName]
|
|
134583
|
+
});
|
|
134584
|
+
const result = await execGit(cmd, context.workingDirectory, context.requestContext, { allowNonZeroExit: true });
|
|
134585
|
+
return parseVerifyOutput(options.tagName, result.stderr, result.exitCode ?? 0);
|
|
134586
|
+
}
|
|
134575
134587
|
default:
|
|
134576
134588
|
throw new Error("Unknown tag operation mode");
|
|
134577
134589
|
}
|
|
@@ -134579,6 +134591,93 @@ async function executeTag(options, context, execGit) {
|
|
|
134579
134591
|
throw mapGitError(error48, "tag");
|
|
134580
134592
|
}
|
|
134581
134593
|
}
|
|
134594
|
+
function parseVerifyOutput(tagName, stderr, exitCode) {
|
|
134595
|
+
if (/^error: tag '.+' not found/m.test(stderr)) {
|
|
134596
|
+
throw new McpError(-32600 /* InvalidRequest */, `Tag not found: ${tagName}`, { tagName });
|
|
134597
|
+
}
|
|
134598
|
+
const base = {
|
|
134599
|
+
mode: "verify",
|
|
134600
|
+
verifiedTag: tagName,
|
|
134601
|
+
rawOutput: stderr
|
|
134602
|
+
};
|
|
134603
|
+
if (/^error: no signature found$/m.test(stderr)) {
|
|
134604
|
+
return {
|
|
134605
|
+
...base,
|
|
134606
|
+
verified: false,
|
|
134607
|
+
warning: "Tag has no signature. Create with a signing key and `GIT_SIGN_COMMITS=true` to produce a signed tag."
|
|
134608
|
+
};
|
|
134609
|
+
}
|
|
134610
|
+
if (/gpg\.ssh\.allowedSignersFile needs to be configured/.test(stderr) || /No principal matched/.test(stderr)) {
|
|
134611
|
+
return {
|
|
134612
|
+
...base,
|
|
134613
|
+
verified: false,
|
|
134614
|
+
signatureType: "ssh",
|
|
134615
|
+
warning: "SSH signature verification requires `gpg.ssh.allowedSignersFile` to be configured. The tag may be validly signed; this environment cannot verify it."
|
|
134616
|
+
};
|
|
134617
|
+
}
|
|
134618
|
+
const gpgBadMatch = /(?:gpg|gpgsm): BAD signature from "([^"]+)"/.exec(stderr);
|
|
134619
|
+
if (gpgBadMatch) {
|
|
134620
|
+
return {
|
|
134621
|
+
...base,
|
|
134622
|
+
verified: false,
|
|
134623
|
+
signatureType: stderr.includes("gpgsm:") ? "x509" : "gpg",
|
|
134624
|
+
signerIdentity: gpgBadMatch[1],
|
|
134625
|
+
warning: "Signature does not validate (BAD signature)."
|
|
134626
|
+
};
|
|
134627
|
+
}
|
|
134628
|
+
const sshBadMatch = /Signature verification failed.*? for "([^"]+)"|Could not verify signature/i.exec(stderr);
|
|
134629
|
+
if (sshBadMatch && exitCode !== 0) {
|
|
134630
|
+
const result = {
|
|
134631
|
+
...base,
|
|
134632
|
+
verified: false,
|
|
134633
|
+
signatureType: "ssh",
|
|
134634
|
+
warning: "SSH signature does not validate."
|
|
134635
|
+
};
|
|
134636
|
+
if (sshBadMatch[1])
|
|
134637
|
+
result.signerIdentity = sshBadMatch[1];
|
|
134638
|
+
return result;
|
|
134639
|
+
}
|
|
134640
|
+
const gpgGoodMatch = /gpg: Good signature from "([^"]+)"/.exec(stderr);
|
|
134641
|
+
if (gpgGoodMatch) {
|
|
134642
|
+
const result = {
|
|
134643
|
+
...base,
|
|
134644
|
+
verified: true,
|
|
134645
|
+
signatureType: "gpg",
|
|
134646
|
+
signerIdentity: gpgGoodMatch[1]
|
|
134647
|
+
};
|
|
134648
|
+
const keyMatch = /using \S+ key ([0-9A-Fa-f]{8,})/.exec(stderr);
|
|
134649
|
+
if (keyMatch)
|
|
134650
|
+
result.signerKey = keyMatch[1];
|
|
134651
|
+
return result;
|
|
134652
|
+
}
|
|
134653
|
+
const x509GoodMatch = /gpgsm: Good signature from "([^"]+)"/.exec(stderr);
|
|
134654
|
+
if (x509GoodMatch) {
|
|
134655
|
+
return {
|
|
134656
|
+
...base,
|
|
134657
|
+
verified: true,
|
|
134658
|
+
signatureType: "x509",
|
|
134659
|
+
signerIdentity: x509GoodMatch[1]
|
|
134660
|
+
};
|
|
134661
|
+
}
|
|
134662
|
+
const sshGoodMatch = /Good "git" signature for (.+?) with \S+ key (SHA256:\S+)/.exec(stderr);
|
|
134663
|
+
if (sshGoodMatch) {
|
|
134664
|
+
return {
|
|
134665
|
+
...base,
|
|
134666
|
+
verified: true,
|
|
134667
|
+
signatureType: "ssh",
|
|
134668
|
+
signerIdentity: sshGoodMatch[1].trim(),
|
|
134669
|
+
signerKey: sshGoodMatch[2]
|
|
134670
|
+
};
|
|
134671
|
+
}
|
|
134672
|
+
if (exitCode === 0) {
|
|
134673
|
+
return { ...base, verified: true };
|
|
134674
|
+
}
|
|
134675
|
+
return {
|
|
134676
|
+
...base,
|
|
134677
|
+
verified: false,
|
|
134678
|
+
warning: "Verification failed but the output format was not recognized. See `rawOutput` for details."
|
|
134679
|
+
};
|
|
134680
|
+
}
|
|
134582
134681
|
// src/services/git/providers/cli/operations/stash/stash.ts
|
|
134583
134682
|
async function executeStash(options, context, execGit) {
|
|
134584
134683
|
try {
|
|
@@ -148014,20 +148113,22 @@ init_zod();
|
|
|
148014
148113
|
init_errors3();
|
|
148015
148114
|
var TOOL_NAME27 = "git_tag";
|
|
148016
148115
|
var TOOL_TITLE27 = "Git Tag";
|
|
148017
|
-
var TOOL_DESCRIPTION27 = "Manage tags: list all tags, create a new tag, or
|
|
148116
|
+
var TOOL_DESCRIPTION27 = "Manage tags: list all tags, create a new tag, delete a tag, or verify a signed tag. Tags are used to mark specific points in history (releases, milestones). Verify runs `git tag -v` and returns a structured result distinguishing unsigned tags, missing trust configuration, bad signatures, and valid signatures.";
|
|
148018
148117
|
var InputSchema27 = exports_external.object({
|
|
148019
148118
|
path: PathSchema,
|
|
148020
|
-
mode: exports_external.enum(["list", "create", "delete"]).default("list").describe("The tag operation to perform."),
|
|
148021
|
-
tagName: TagNameSchema.optional().describe("Tag name for create/delete operations."),
|
|
148119
|
+
mode: exports_external.enum(["list", "create", "delete", "verify"]).default("list").describe("The tag operation to perform."),
|
|
148120
|
+
tagName: TagNameSchema.optional().describe("Tag name for create/delete/verify operations."),
|
|
148022
148121
|
commit: CommitRefSchema.optional().describe("Commit to tag (default: HEAD for create operation)."),
|
|
148023
148122
|
message: exports_external.string().optional().describe("Tag message. Providing a message always produces an annotated tag (git does not support messages on lightweight tags). For release tags, summarize notable changes."),
|
|
148024
148123
|
annotated: exports_external.boolean().default(false).describe('Create an annotated tag with a default "Tag <name>" message. Only effective when no message is provided and signing is disabled — otherwise the tag is always annotated.'),
|
|
148025
|
-
force: ForceSchema.describe("Overwrite an existing tag (create mode only; has no effect on list or delete).")
|
|
148124
|
+
force: ForceSchema.describe("Overwrite an existing tag (create mode only; has no effect on list or delete)."),
|
|
148125
|
+
limit: LimitSchema.describe("For list mode: cap the number of tags returned (applied at the git command via `--count=N`). Use on repos with many tags.")
|
|
148026
148126
|
});
|
|
148027
148127
|
var TagInfoSchema = exports_external.object({
|
|
148028
148128
|
name: exports_external.string().describe("Tag name."),
|
|
148029
148129
|
commit: exports_external.string().describe("Commit hash the tag points to."),
|
|
148030
|
-
message: exports_external.string().optional().describe("
|
|
148130
|
+
message: exports_external.string().optional().describe("First line of the tag annotation (annotated tags only). See `annotationBody` for the remainder."),
|
|
148131
|
+
annotationBody: exports_external.string().optional().describe("Remaining annotation body after the subject line (annotated tags only)."),
|
|
148031
148132
|
tagger: exports_external.string().optional().describe("Tagger name and email."),
|
|
148032
148133
|
timestamp: exports_external.number().int().optional().describe("Tag creation timestamp.")
|
|
148033
148134
|
});
|
|
@@ -148038,10 +148139,17 @@ var OutputSchema28 = exports_external.object({
|
|
|
148038
148139
|
created: exports_external.string().optional().describe("Created tag name (for create mode)."),
|
|
148039
148140
|
deleted: exports_external.string().optional().describe("Deleted tag name (for delete mode)."),
|
|
148040
148141
|
signed: exports_external.boolean().optional().describe("Whether the created tag was signed. Only populated for create mode. False when GIT_SIGN_COMMITS=false or when signing failed and fell back to unsigned."),
|
|
148041
|
-
signingWarning: exports_external.string().optional().describe("Populated only when signing was requested but failed, and the tag was created unsigned as a fallback.")
|
|
148142
|
+
signingWarning: exports_external.string().optional().describe("Populated only when signing was requested but failed, and the tag was created unsigned as a fallback."),
|
|
148143
|
+
verifiedTag: exports_external.string().optional().describe("Verified tag name (for verify mode). Echoes the input so callers can correlate results in batched flows."),
|
|
148144
|
+
verified: exports_external.boolean().optional().describe("Whether the signature validated (for verify mode). `false` for unsigned tags, missing trust config, bad signatures, or unparseable output — inspect `warning` to distinguish."),
|
|
148145
|
+
signatureType: exports_external.enum(["gpg", "ssh", "x509"]).optional().describe("Signature algorithm family when detectable from `git tag -v` output (verify mode). Absent for unsigned tags or unparseable output."),
|
|
148146
|
+
signerIdentity: exports_external.string().optional().describe("Signer identity as emitted by git — e.g., `Name <email>` for GPG or the SSH principal. Verify mode only."),
|
|
148147
|
+
signerKey: exports_external.string().optional().describe("Key material emitted by git — GPG fingerprint/key ID or SSH key fingerprint (`SHA256:…`). Verify mode only; absent when git did not surface it."),
|
|
148148
|
+
warning: exports_external.string().optional().describe("Populated on verify failure with a human-readable reason distinguishing unsigned tags, missing trust configuration, bad signatures, and unparseable output."),
|
|
148149
|
+
rawOutput: exports_external.string().optional().describe("Raw stderr from `git tag -v` for callers that need the full verification output (verify mode only).")
|
|
148042
148150
|
});
|
|
148043
148151
|
async function gitTagLogic(input, { provider, targetPath, appContext }) {
|
|
148044
|
-
if ((input.mode === "create" || input.mode === "delete") && !input.tagName) {
|
|
148152
|
+
if ((input.mode === "create" || input.mode === "delete" || input.mode === "verify") && !input.tagName) {
|
|
148045
148153
|
throw new McpError(-32602 /* InvalidParams */, `Tag name is required for ${input.mode} operation.`);
|
|
148046
148154
|
}
|
|
148047
148155
|
const tagOptions = {
|
|
@@ -148058,6 +148166,9 @@ async function gitTagLogic(input, { provider, targetPath, appContext }) {
|
|
|
148058
148166
|
if (input.message !== undefined) {
|
|
148059
148167
|
tagOptions.message = normalizeMessage(input.message);
|
|
148060
148168
|
}
|
|
148169
|
+
if (input.limit !== undefined) {
|
|
148170
|
+
tagOptions.limit = input.limit;
|
|
148171
|
+
}
|
|
148061
148172
|
const result = await provider.tag(tagOptions, {
|
|
148062
148173
|
workingDirectory: targetPath,
|
|
148063
148174
|
requestContext: appContext,
|
|
@@ -148076,6 +148187,27 @@ async function gitTagLogic(input, { provider, targetPath, appContext }) {
|
|
|
148076
148187
|
if (result.signingWarning) {
|
|
148077
148188
|
output.signingWarning = result.signingWarning;
|
|
148078
148189
|
}
|
|
148190
|
+
if (result.verifiedTag !== undefined) {
|
|
148191
|
+
output.verifiedTag = result.verifiedTag;
|
|
148192
|
+
}
|
|
148193
|
+
if (result.verified !== undefined) {
|
|
148194
|
+
output.verified = result.verified;
|
|
148195
|
+
}
|
|
148196
|
+
if (result.signatureType !== undefined) {
|
|
148197
|
+
output.signatureType = result.signatureType;
|
|
148198
|
+
}
|
|
148199
|
+
if (result.signerIdentity !== undefined) {
|
|
148200
|
+
output.signerIdentity = result.signerIdentity;
|
|
148201
|
+
}
|
|
148202
|
+
if (result.signerKey !== undefined) {
|
|
148203
|
+
output.signerKey = result.signerKey;
|
|
148204
|
+
}
|
|
148205
|
+
if (result.warning !== undefined) {
|
|
148206
|
+
output.warning = result.warning;
|
|
148207
|
+
}
|
|
148208
|
+
if (result.rawOutput !== undefined) {
|
|
148209
|
+
output.rawOutput = result.rawOutput;
|
|
148210
|
+
}
|
|
148079
148211
|
return output;
|
|
148080
148212
|
}
|
|
148081
148213
|
function filterGitTagOutput(result, level) {
|
|
@@ -148084,10 +148216,16 @@ function filterGitTagOutput(result, level) {
|
|
|
148084
148216
|
success: result.success,
|
|
148085
148217
|
mode: result.mode,
|
|
148086
148218
|
...result.signed !== undefined && { signed: result.signed },
|
|
148087
|
-
...result.signingWarning && { signingWarning: result.signingWarning }
|
|
148219
|
+
...result.signingWarning && { signingWarning: result.signingWarning },
|
|
148220
|
+
...result.verified !== undefined && { verified: result.verified },
|
|
148221
|
+
...result.warning && { warning: result.warning }
|
|
148088
148222
|
};
|
|
148089
148223
|
}
|
|
148090
|
-
|
|
148224
|
+
if (level === "full") {
|
|
148225
|
+
return result;
|
|
148226
|
+
}
|
|
148227
|
+
const { rawOutput: _raw, ...rest } = result;
|
|
148228
|
+
return rest;
|
|
148091
148229
|
}
|
|
148092
148230
|
var responseFormatter27 = createJsonFormatter({
|
|
148093
148231
|
filter: filterGitTagOutput
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cyanheads/git-mcp-server",
|
|
3
|
-
"version": "2.14.
|
|
3
|
+
"version": "2.14.2",
|
|
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",
|