@cyanheads/git-mcp-server 2.10.0 → 2.10.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 +60 -60
- package/dist/index.js +110 -38
- package/package.json +3 -3
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,27 +17,27 @@
|
|
|
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, stash changes, reset state, manage worktrees, set/clear session directory |
|
|
29
29
|
|
|
30
30
|
## Resources
|
|
31
31
|
|
|
32
|
-
| Resource | URI | Description
|
|
33
|
-
| :------------------------ | :------------------------ |
|
|
34
|
-
| **Git Working Directory** | `git://working-directory` | The current session working directory, set via `git_set_working_dir`.
|
|
32
|
+
| Resource | URI | Description |
|
|
33
|
+
| :------------------------ | :------------------------ | :-------------------------------------------------------------------- |
|
|
34
|
+
| **Git Working Directory** | `git://working-directory` | The current session working directory, set via `git_set_working_dir`. |
|
|
35
35
|
|
|
36
36
|
## Prompts
|
|
37
37
|
|
|
38
|
-
| Prompt | Description
|
|
39
|
-
| :-------------- |
|
|
40
|
-
| **Git Wrap-up** | Workflow protocol for completing git sessions: review, document, commit, and tag changes.
|
|
38
|
+
| Prompt | Description | Parameters |
|
|
39
|
+
| :-------------- | :---------------------------------------------------------------------------------------- | :--------------------------------------------------------------------- |
|
|
40
|
+
| **Git Wrap-up** | Workflow protocol for completing git sessions: review, document, commit, and tag changes. | `changelogPath`, `skipDocumentation`, `createTag`, `updateAgentFiles`. |
|
|
41
41
|
|
|
42
42
|
## Getting started
|
|
43
43
|
|
|
@@ -83,20 +83,20 @@ For Streamable HTTP, set `MCP_TRANSPORT_TYPE=http` and `MCP_HTTP_PORT=3015`.
|
|
|
83
83
|
|
|
84
84
|
Built on [`mcp-ts-template`](https://github.com/cyanheads/mcp-ts-template).
|
|
85
85
|
|
|
86
|
-
| Feature
|
|
87
|
-
|
|
|
88
|
-
| Declarative tools
|
|
89
|
-
| Error handling
|
|
90
|
-
| Authentication
|
|
91
|
-
| Pluggable storage
|
|
92
|
-
| Observability
|
|
93
|
-
| Dependency injection
|
|
94
|
-
| Cross-runtime
|
|
95
|
-
| Provider architecture
|
|
96
|
-
| Working directory management | Session-specific directory context for multi-repo workflows.
|
|
97
|
-
| Configurable git identity
|
|
98
|
-
| Commit signing
|
|
99
|
-
| Safety
|
|
86
|
+
| Feature | Details |
|
|
87
|
+
| :--------------------------- | :------------------------------------------------------------------------------------------------------------------ |
|
|
88
|
+
| Declarative tools | Define capabilities in single, self-contained files. The framework handles registration, validation, and execution. |
|
|
89
|
+
| Error handling | Unified `McpError` system for consistent, structured error responses. |
|
|
90
|
+
| Authentication | Supports `none`, `jwt`, and `oauth` modes. |
|
|
91
|
+
| Pluggable storage | Swap backends (`in-memory`, `filesystem`, `Supabase`, `Cloudflare KV/R2`) without changing business logic. |
|
|
92
|
+
| Observability | Structured logging (Pino) and optional auto-instrumented OpenTelemetry for traces and metrics. |
|
|
93
|
+
| Dependency injection | Built with `tsyringe` for decoupled, testable architecture. |
|
|
94
|
+
| Cross-runtime | Auto-detects Bun or Node.js and uses the appropriate process spawning method. |
|
|
95
|
+
| Provider architecture | Pluggable git provider system. Current: CLI. Planned: isomorphic-git for edge deployment. |
|
|
96
|
+
| Working directory management | Session-specific directory context for multi-repo workflows. |
|
|
97
|
+
| Configurable git identity | Override author/committer info via environment variables, with fallback to global git config. |
|
|
98
|
+
| Commit signing | Optional GPG/SSH signing for commits, merges, rebases, cherry-picks, and tags. |
|
|
99
|
+
| Safety | Destructive operations (`git clean`, `git reset --hard`) require explicit confirmation flags. |
|
|
100
100
|
|
|
101
101
|
## Security
|
|
102
102
|
|
|
@@ -111,26 +111,26 @@ Built on [`mcp-ts-template`](https://github.com/cyanheads/mcp-ts-template).
|
|
|
111
111
|
|
|
112
112
|
All configuration is validated at startup in `src/config/index.ts`. Key environment variables:
|
|
113
113
|
|
|
114
|
-
| Variable | Description
|
|
115
|
-
| :----------------------------- |
|
|
116
|
-
| `MCP_TRANSPORT_TYPE` | Transport: `stdio` or `http`.
|
|
117
|
-
| `MCP_SESSION_MODE` | HTTP session mode: `stateless`, `stateful`, or `auto`.
|
|
118
|
-
| `MCP_RESPONSE_FORMAT` | Response format: `json` (LLM-optimized), `markdown` (human-readable), or `auto`.
|
|
119
|
-
| `MCP_RESPONSE_VERBOSITY` | Detail level: `minimal`, `standard`, or `full`.
|
|
120
|
-
| `MCP_HTTP_PORT` | HTTP server port.
|
|
121
|
-
| `MCP_HTTP_HOST` | HTTP server hostname.
|
|
122
|
-
| `MCP_HTTP_ENDPOINT_PATH` | MCP request endpoint path.
|
|
123
|
-
| `MCP_AUTH_MODE` | Authentication mode: `none`, `jwt`, or `oauth`.
|
|
124
|
-
| `STORAGE_PROVIDER_TYPE` | Storage backend: `in-memory`, `filesystem`, `supabase`, `cloudflare-kv`, `r2`.
|
|
125
|
-
| `OTEL_ENABLED` | Enable OpenTelemetry.
|
|
126
|
-
| `MCP_LOG_LEVEL` | Minimum log level: `debug`, `info`, `warn`, `error`.
|
|
127
|
-
| `GIT_SIGN_COMMITS` | Enable GPG/SSH signing for commits, merges, rebases, cherry-picks, and tags.
|
|
128
|
-
| `GIT_AUTHOR_NAME` | Git author name. Aliases: `GIT_USERNAME`, `GIT_USER`. Falls back to global git config.
|
|
129
|
-
| `GIT_AUTHOR_EMAIL` | Git author email. Aliases: `GIT_EMAIL`, `GIT_USER_EMAIL`. Falls back to global git config.
|
|
130
|
-
| `GIT_BASE_DIR` | Absolute path to restrict all git operations to a specific directory tree.
|
|
131
|
-
| `GIT_WRAPUP_INSTRUCTIONS_PATH` | Path to custom markdown file with workflow instructions.
|
|
132
|
-
| `MCP_AUTH_SECRET_KEY` | Required for `jwt` auth. 32+ character secret key.
|
|
133
|
-
| `OAUTH_ISSUER_URL` | Required for `oauth` auth. OIDC provider URL.
|
|
114
|
+
| Variable | Description | Default |
|
|
115
|
+
| :----------------------------- | :----------------------------------------------------------------------------------------- | :---------- |
|
|
116
|
+
| `MCP_TRANSPORT_TYPE` | Transport: `stdio` or `http`. | `stdio` |
|
|
117
|
+
| `MCP_SESSION_MODE` | HTTP session mode: `stateless`, `stateful`, or `auto`. | `auto` |
|
|
118
|
+
| `MCP_RESPONSE_FORMAT` | Response format: `json` (LLM-optimized), `markdown` (human-readable), or `auto`. | `json` |
|
|
119
|
+
| `MCP_RESPONSE_VERBOSITY` | Detail level: `minimal`, `standard`, or `full`. | `standard` |
|
|
120
|
+
| `MCP_HTTP_PORT` | HTTP server port. | `3015` |
|
|
121
|
+
| `MCP_HTTP_HOST` | HTTP server hostname. | `127.0.0.1` |
|
|
122
|
+
| `MCP_HTTP_ENDPOINT_PATH` | MCP request endpoint path. | `/mcp` |
|
|
123
|
+
| `MCP_AUTH_MODE` | Authentication mode: `none`, `jwt`, or `oauth`. | `none` |
|
|
124
|
+
| `STORAGE_PROVIDER_TYPE` | Storage backend: `in-memory`, `filesystem`, `supabase`, `cloudflare-kv`, `r2`. | `in-memory` |
|
|
125
|
+
| `OTEL_ENABLED` | Enable OpenTelemetry. | `false` |
|
|
126
|
+
| `MCP_LOG_LEVEL` | Minimum log level: `debug`, `info`, `warn`, `error`. | `info` |
|
|
127
|
+
| `GIT_SIGN_COMMITS` | Enable GPG/SSH signing for commits, merges, rebases, cherry-picks, and tags. | `false` |
|
|
128
|
+
| `GIT_AUTHOR_NAME` | Git author name. Aliases: `GIT_USERNAME`, `GIT_USER`. Falls back to global git config. | `(none)` |
|
|
129
|
+
| `GIT_AUTHOR_EMAIL` | Git author email. Aliases: `GIT_EMAIL`, `GIT_USER_EMAIL`. Falls back to global git config. | `(none)` |
|
|
130
|
+
| `GIT_BASE_DIR` | Absolute path to restrict all git operations to a specific directory tree. | `(none)` |
|
|
131
|
+
| `GIT_WRAPUP_INSTRUCTIONS_PATH` | Path to custom markdown file with workflow instructions. | `(none)` |
|
|
132
|
+
| `MCP_AUTH_SECRET_KEY` | Required for `jwt` auth. 32+ character secret key. | `(none)` |
|
|
133
|
+
| `OAUTH_ISSUER_URL` | Required for `oauth` auth. OIDC provider URL. | `(none)` |
|
|
134
134
|
|
|
135
135
|
## Running the server
|
|
136
136
|
|
|
@@ -167,17 +167,17 @@ npm run deploy:prod # Deploy to Cloudflare
|
|
|
167
167
|
|
|
168
168
|
## Project structure
|
|
169
169
|
|
|
170
|
-
| Directory | Purpose
|
|
171
|
-
| :-------------------------- |
|
|
172
|
-
| `src/mcp-server/tools` | Tool definitions (`*.tool.ts`). Git capabilities live here.
|
|
170
|
+
| Directory | Purpose |
|
|
171
|
+
| :-------------------------- | :---------------------------------------------------------------- |
|
|
172
|
+
| `src/mcp-server/tools` | Tool definitions (`*.tool.ts`). Git capabilities live here. |
|
|
173
173
|
| `src/mcp-server/resources` | Resource definitions (`*.resource.ts`). Git context data sources. |
|
|
174
|
-
| `src/mcp-server/transports` | HTTP and STDIO transport implementations, including auth.
|
|
175
|
-
| `src/storage` | `StorageService` abstraction and provider implementations.
|
|
176
|
-
| `src/services` | Git service provider (CLI-based git operations).
|
|
177
|
-
| `src/container` | DI container registrations and tokens.
|
|
178
|
-
| `src/utils` | Logging, error handling, performance, security utilities.
|
|
179
|
-
| `src/config` | Environment variable parsing and validation (Zod).
|
|
180
|
-
| `tests/` | Unit and integration tests, mirroring `src/` structure.
|
|
174
|
+
| `src/mcp-server/transports` | HTTP and STDIO transport implementations, including auth. |
|
|
175
|
+
| `src/storage` | `StorageService` abstraction and provider implementations. |
|
|
176
|
+
| `src/services` | Git service provider (CLI-based git operations). |
|
|
177
|
+
| `src/container` | DI container registrations and tokens. |
|
|
178
|
+
| `src/utils` | Logging, error handling, performance, security utilities. |
|
|
179
|
+
| `src/config` | Environment variable parsing and validation (Zod). |
|
|
180
|
+
| `tests/` | Unit and integration tests, mirroring `src/` structure. |
|
|
181
181
|
|
|
182
182
|
## Response format
|
|
183
183
|
|
package/dist/index.js
CHANGED
|
@@ -15335,7 +15335,7 @@ var package_default;
|
|
|
15335
15335
|
var init_package = __esm(() => {
|
|
15336
15336
|
package_default = {
|
|
15337
15337
|
name: "@cyanheads/git-mcp-server",
|
|
15338
|
-
version: "2.10.
|
|
15338
|
+
version: "2.10.2",
|
|
15339
15339
|
mcpName: "io.github.cyanheads/git-mcp-server",
|
|
15340
15340
|
description: "A secure and scalable Git MCP server enabling AI agents to perform comprehensive Git version control operations via STDIO and Streamable HTTP.",
|
|
15341
15341
|
main: "dist/index.js",
|
|
@@ -15407,7 +15407,7 @@ var init_package = __esm(() => {
|
|
|
15407
15407
|
rollup: "4.59.0"
|
|
15408
15408
|
},
|
|
15409
15409
|
devDependencies: {
|
|
15410
|
-
"@cloudflare/workers-types": "^4.
|
|
15410
|
+
"@cloudflare/workers-types": "^4.20260307.1",
|
|
15411
15411
|
"@eslint/js": "^10.0.1",
|
|
15412
15412
|
"@hono/mcp": "^0.2.4",
|
|
15413
15413
|
"@hono/node-server": "1.19.11",
|
|
@@ -15431,7 +15431,7 @@ var init_package = __esm(() => {
|
|
|
15431
15431
|
"bun-types": "^1.3.10",
|
|
15432
15432
|
depcheck: "^1.4.7",
|
|
15433
15433
|
dotenv: "^17.3.1",
|
|
15434
|
-
eslint: "^10.0.
|
|
15434
|
+
eslint: "^10.0.3",
|
|
15435
15435
|
execa: "^9.6.1",
|
|
15436
15436
|
globals: "^17.4.0",
|
|
15437
15437
|
hono: "4.12.5",
|
|
@@ -15576,7 +15576,7 @@ __export(exports_config, {
|
|
|
15576
15576
|
});
|
|
15577
15577
|
import { homedir } from "os";
|
|
15578
15578
|
import path from "node:path";
|
|
15579
|
-
var import_dotenv, packageManifest,
|
|
15579
|
+
var import_dotenv, packageManifest, emptyStringAsUndefined = (val) => {
|
|
15580
15580
|
if (typeof val === "string" && val.trim() === "") {
|
|
15581
15581
|
return;
|
|
15582
15582
|
}
|
|
@@ -15680,7 +15680,7 @@ var import_dotenv, packageManifest, hasFileSystemAccess, emptyStringAsUndefined
|
|
|
15680
15680
|
const finalRawConfig = {
|
|
15681
15681
|
...rawConfig,
|
|
15682
15682
|
pkg: parsedPkg,
|
|
15683
|
-
logsPath: rawConfig.logsPath
|
|
15683
|
+
logsPath: rawConfig.logsPath,
|
|
15684
15684
|
mcpServerName: env.MCP_SERVER_NAME ?? parsedPkg.name,
|
|
15685
15685
|
mcpServerVersion: env.MCP_SERVER_VERSION ?? parsedPkg.version,
|
|
15686
15686
|
mcpServerDescription: env.MCP_SERVER_DESCRIPTION ?? parsedPkg.description,
|
|
@@ -15707,7 +15707,6 @@ var init_config = __esm(() => {
|
|
|
15707
15707
|
init_errors3();
|
|
15708
15708
|
import_dotenv = __toESM(require_main(), 1);
|
|
15709
15709
|
packageManifest = package_default;
|
|
15710
|
-
hasFileSystemAccess = typeof process !== "undefined" && typeof process.versions === "object" && process.versions !== null && typeof process.versions.node === "string";
|
|
15711
15710
|
import_dotenv.default.config({ quiet: true });
|
|
15712
15711
|
ConfigSchema = exports_external.object({
|
|
15713
15712
|
pkg: exports_external.object({
|
|
@@ -136778,53 +136777,76 @@ async function executeShow(options, context, execGit) {
|
|
|
136778
136777
|
// src/services/git/providers/cli/operations/commits/diff.ts
|
|
136779
136778
|
async function executeDiff(options, context, execGit) {
|
|
136780
136779
|
try {
|
|
136781
|
-
const
|
|
136780
|
+
const baseFlags = [];
|
|
136782
136781
|
const pathArgs = [];
|
|
136783
136782
|
if (options.staged) {
|
|
136784
|
-
|
|
136783
|
+
baseFlags.push("--cached");
|
|
136785
136784
|
}
|
|
136785
|
+
if (options.commit1) {
|
|
136786
|
+
baseFlags.push(options.commit1);
|
|
136787
|
+
}
|
|
136788
|
+
if (options.commit2) {
|
|
136789
|
+
baseFlags.push(options.commit2);
|
|
136790
|
+
}
|
|
136791
|
+
const flags = [...baseFlags];
|
|
136786
136792
|
if (options.nameOnly) {
|
|
136787
136793
|
flags.push("--name-only");
|
|
136788
136794
|
}
|
|
136789
136795
|
if (options.unified !== undefined) {
|
|
136790
136796
|
flags.push(`--unified=${options.unified}`);
|
|
136791
136797
|
}
|
|
136792
|
-
if (options.
|
|
136793
|
-
|
|
136794
|
-
|
|
136795
|
-
|
|
136796
|
-
|
|
136798
|
+
if (options.paths?.length || options.excludePatterns?.length) {
|
|
136799
|
+
pathArgs.push("--");
|
|
136800
|
+
if (options.paths?.length) {
|
|
136801
|
+
pathArgs.push(...options.paths);
|
|
136802
|
+
}
|
|
136803
|
+
if (options.excludePatterns?.length) {
|
|
136804
|
+
pathArgs.push(...options.excludePatterns.map((p) => `:(exclude)${p}`));
|
|
136805
|
+
}
|
|
136797
136806
|
}
|
|
136798
|
-
|
|
136799
|
-
|
|
136807
|
+
let excludedFiles;
|
|
136808
|
+
if (options.excludePatterns?.length) {
|
|
136809
|
+
const checkCmd = buildGitCommand({
|
|
136810
|
+
command: "diff",
|
|
136811
|
+
args: [...baseFlags, "--name-only", "--", ...options.excludePatterns]
|
|
136812
|
+
});
|
|
136813
|
+
const checkResult = await execGit(checkCmd, context.workingDirectory, context.requestContext);
|
|
136814
|
+
const matched = checkResult.stdout.split(`
|
|
136815
|
+
`).filter((f3) => f3.trim());
|
|
136816
|
+
if (matched.length > 0) {
|
|
136817
|
+
excludedFiles = matched;
|
|
136818
|
+
}
|
|
136800
136819
|
}
|
|
136801
136820
|
if (options.stat) {
|
|
136802
|
-
const statFlags2 = flags.filter((f3) => f3 !== "--name-only" && !f3.startsWith("--unified="));
|
|
136803
136821
|
const statCmd2 = buildGitCommand({
|
|
136804
136822
|
command: "diff",
|
|
136805
|
-
args: [...
|
|
136823
|
+
args: [...baseFlags, "--stat", ...pathArgs]
|
|
136806
136824
|
});
|
|
136807
136825
|
const statResult2 = await execGit(statCmd2, context.workingDirectory, context.requestContext);
|
|
136808
136826
|
const stats2 = parseGitDiffStat(statResult2.stdout);
|
|
136809
|
-
let
|
|
136827
|
+
let untrackedStatOutput2 = "";
|
|
136810
136828
|
let untrackedFileCount2 = 0;
|
|
136811
136829
|
if (options.includeUntracked) {
|
|
136812
|
-
|
|
136830
|
+
let untrackedFiles = await getUntrackedFiles(execGit, context);
|
|
136831
|
+
if (options.excludePatterns?.length) {
|
|
136832
|
+
({ files: untrackedFiles, excludedFiles } = applyUntrackedExclusions(untrackedFiles, options.excludePatterns, excludedFiles));
|
|
136833
|
+
}
|
|
136813
136834
|
for (const file2 of untrackedFiles) {
|
|
136814
136835
|
const result = await execUntrackedDiff(execGit, context, file2, true);
|
|
136815
136836
|
if (result) {
|
|
136816
|
-
|
|
136837
|
+
untrackedStatOutput2 += result;
|
|
136817
136838
|
untrackedFileCount2++;
|
|
136818
136839
|
}
|
|
136819
136840
|
}
|
|
136820
136841
|
}
|
|
136821
|
-
const
|
|
136842
|
+
const untrackedStats2 = untrackedStatOutput2 ? parseGitDiffStat(untrackedStatOutput2) : { totalAdditions: 0, totalDeletions: 0 };
|
|
136822
136843
|
return {
|
|
136823
|
-
diff: statResult2.stdout +
|
|
136844
|
+
diff: statResult2.stdout + untrackedStatOutput2,
|
|
136824
136845
|
filesChanged: stats2.files.length + untrackedFileCount2,
|
|
136825
|
-
insertions: stats2.totalAdditions +
|
|
136826
|
-
deletions: stats2.totalDeletions +
|
|
136827
|
-
binary: statResult2.stdout.includes("Binary files") ||
|
|
136846
|
+
insertions: stats2.totalAdditions + untrackedStats2.totalAdditions,
|
|
136847
|
+
deletions: stats2.totalDeletions + untrackedStats2.totalDeletions,
|
|
136848
|
+
binary: statResult2.stdout.includes("Binary files") || untrackedStatOutput2.includes("Binary files"),
|
|
136849
|
+
...excludedFiles && { excludedFiles }
|
|
136828
136850
|
};
|
|
136829
136851
|
}
|
|
136830
136852
|
const args = [...flags, ...pathArgs];
|
|
@@ -136832,8 +136854,12 @@ async function executeDiff(options, context, execGit) {
|
|
|
136832
136854
|
const diffResult = await execGit(diffCmd, context.workingDirectory, context.requestContext);
|
|
136833
136855
|
let untrackedDiff = "";
|
|
136834
136856
|
let untrackedFileCount = 0;
|
|
136857
|
+
let untrackedStatOutput = "";
|
|
136835
136858
|
if (options.includeUntracked) {
|
|
136836
|
-
|
|
136859
|
+
let untrackedFiles = await getUntrackedFiles(execGit, context);
|
|
136860
|
+
if (options.excludePatterns?.length) {
|
|
136861
|
+
({ files: untrackedFiles, excludedFiles } = applyUntrackedExclusions(untrackedFiles, options.excludePatterns, excludedFiles));
|
|
136862
|
+
}
|
|
136837
136863
|
untrackedFileCount = untrackedFiles.length;
|
|
136838
136864
|
for (const file2 of untrackedFiles) {
|
|
136839
136865
|
if (options.nameOnly) {
|
|
@@ -136844,6 +136870,10 @@ async function executeDiff(options, context, execGit) {
|
|
|
136844
136870
|
if (result) {
|
|
136845
136871
|
untrackedDiff += result;
|
|
136846
136872
|
}
|
|
136873
|
+
const statResult2 = await execUntrackedDiff(execGit, context, file2, true);
|
|
136874
|
+
if (statResult2) {
|
|
136875
|
+
untrackedStatOutput += statResult2;
|
|
136876
|
+
}
|
|
136847
136877
|
}
|
|
136848
136878
|
}
|
|
136849
136879
|
}
|
|
@@ -136854,23 +136884,25 @@ async function executeDiff(options, context, execGit) {
|
|
|
136854
136884
|
return {
|
|
136855
136885
|
diff: combinedDiff,
|
|
136856
136886
|
filesChanged: files.length,
|
|
136857
|
-
binary: false
|
|
136887
|
+
binary: false,
|
|
136888
|
+
...excludedFiles && { excludedFiles }
|
|
136858
136889
|
};
|
|
136859
136890
|
}
|
|
136860
|
-
const statFlags = flags.filter((f3) => f3 !== "--name-only" && !f3.startsWith("--unified="));
|
|
136861
136891
|
const statCmd = buildGitCommand({
|
|
136862
136892
|
command: "diff",
|
|
136863
|
-
args: [...
|
|
136893
|
+
args: [...baseFlags, "--stat", ...pathArgs]
|
|
136864
136894
|
});
|
|
136865
136895
|
const statResult = await execGit(statCmd, context.workingDirectory, context.requestContext);
|
|
136866
136896
|
const stats = parseGitDiffStat(statResult.stdout);
|
|
136897
|
+
const untrackedStats = untrackedStatOutput ? parseGitDiffStat(untrackedStatOutput) : { totalAdditions: 0, totalDeletions: 0 };
|
|
136867
136898
|
const hasBinary = combinedDiff.includes("Binary files");
|
|
136868
136899
|
return {
|
|
136869
136900
|
diff: combinedDiff,
|
|
136870
136901
|
filesChanged: stats.files.length + untrackedFileCount,
|
|
136871
|
-
insertions: stats.totalAdditions,
|
|
136872
|
-
deletions: stats.totalDeletions,
|
|
136873
|
-
binary: hasBinary
|
|
136902
|
+
insertions: stats.totalAdditions + untrackedStats.totalAdditions,
|
|
136903
|
+
deletions: stats.totalDeletions + untrackedStats.totalDeletions,
|
|
136904
|
+
binary: hasBinary,
|
|
136905
|
+
...excludedFiles && { excludedFiles }
|
|
136874
136906
|
};
|
|
136875
136907
|
} catch (error48) {
|
|
136876
136908
|
throw mapGitError(error48, "diff");
|
|
@@ -136903,6 +136935,18 @@ async function execUntrackedDiff(execGit, context, file2, stat2) {
|
|
|
136903
136935
|
return null;
|
|
136904
136936
|
}
|
|
136905
136937
|
}
|
|
136938
|
+
function applyUntrackedExclusions(files, patterns, currentExcluded) {
|
|
136939
|
+
const patternSet = new Set(patterns);
|
|
136940
|
+
const matched = files.filter((f3) => patternSet.has(f3));
|
|
136941
|
+
if (matched.length === 0) {
|
|
136942
|
+
return { files, excludedFiles: currentExcluded };
|
|
136943
|
+
}
|
|
136944
|
+
const matchedSet = new Set(matched);
|
|
136945
|
+
return {
|
|
136946
|
+
files: files.filter((f3) => !matchedSet.has(f3)),
|
|
136947
|
+
excludedFiles: [...currentExcluded || [], ...matched]
|
|
136948
|
+
};
|
|
136949
|
+
}
|
|
136906
136950
|
// src/services/git/providers/cli/operations/branches/branch.ts
|
|
136907
136951
|
async function executeBranch(options, context, execGit) {
|
|
136908
136952
|
try {
|
|
@@ -149713,6 +149757,25 @@ init_zod();
|
|
|
149713
149757
|
var TOOL_NAME13 = "git_diff";
|
|
149714
149758
|
var TOOL_TITLE13 = "Git Diff";
|
|
149715
149759
|
var TOOL_DESCRIPTION13 = "View differences between commits, branches, or working tree. Shows changes in unified diff format.";
|
|
149760
|
+
var AUTO_EXCLUDE_PATTERNS = [
|
|
149761
|
+
"package-lock.json",
|
|
149762
|
+
"yarn.lock",
|
|
149763
|
+
"pnpm-lock.yaml",
|
|
149764
|
+
"bun.lock",
|
|
149765
|
+
"bun.lockb",
|
|
149766
|
+
"poetry.lock",
|
|
149767
|
+
"Pipfile.lock",
|
|
149768
|
+
"uv.lock",
|
|
149769
|
+
"composer.lock",
|
|
149770
|
+
"Gemfile.lock",
|
|
149771
|
+
"go.sum",
|
|
149772
|
+
"Cargo.lock",
|
|
149773
|
+
"flake.lock",
|
|
149774
|
+
"pubspec.lock",
|
|
149775
|
+
"mix.lock",
|
|
149776
|
+
"Podfile.lock",
|
|
149777
|
+
"packages.lock.json"
|
|
149778
|
+
];
|
|
149716
149779
|
var InputSchema13 = exports_external.object({
|
|
149717
149780
|
path: PathSchema,
|
|
149718
149781
|
target: CommitRefSchema.optional().describe("Target commit/branch to compare against. If not specified, shows unstaged changes in working tree."),
|
|
@@ -149722,14 +149785,16 @@ var InputSchema13 = exports_external.object({
|
|
|
149722
149785
|
includeUntracked: exports_external.boolean().default(false).describe("Include untracked files in the diff. Useful for reviewing all upcoming changes."),
|
|
149723
149786
|
nameOnly: exports_external.boolean().default(false).describe("Show only names of changed files, not the diff content."),
|
|
149724
149787
|
stat: exports_external.boolean().default(false).describe("Show diffstat (summary of changes) instead of full diff content."),
|
|
149725
|
-
contextLines: exports_external.number().int().min(0).max(100).default(3).describe("Number of context lines to show around changes.")
|
|
149788
|
+
contextLines: exports_external.number().int().min(0).max(100).default(3).describe("Number of context lines to show around changes."),
|
|
149789
|
+
autoExclude: exports_external.boolean().default(true).describe("Automatically exclude lock files and other generated files (e.g., package-lock.json, yarn.lock, bun.lock, poetry.lock, go.sum) from diff output to reduce context bloat. Set to false if you need to inspect these files.")
|
|
149726
149790
|
});
|
|
149727
149791
|
var OutputSchema14 = exports_external.object({
|
|
149728
149792
|
success: exports_external.boolean().describe("Indicates if the operation was successful."),
|
|
149729
149793
|
diff: exports_external.string().describe("The diff output in unified diff format."),
|
|
149730
149794
|
filesChanged: exports_external.number().int().describe("Number of files with differences."),
|
|
149731
149795
|
insertions: exports_external.number().int().optional().describe("Total number of line insertions."),
|
|
149732
|
-
deletions: exports_external.number().int().optional().describe("Total number of line deletions.")
|
|
149796
|
+
deletions: exports_external.number().int().optional().describe("Total number of line deletions."),
|
|
149797
|
+
excludedFiles: exports_external.array(exports_external.string()).optional().describe("Files that were automatically excluded from the diff (e.g., lock files). Call again with autoExclude=false to include them.")
|
|
149733
149798
|
});
|
|
149734
149799
|
async function gitDiffLogic(input, { provider, targetPath, appContext }) {
|
|
149735
149800
|
const result = await provider.diff({
|
|
@@ -149740,7 +149805,10 @@ async function gitDiffLogic(input, { provider, targetPath, appContext }) {
|
|
|
149740
149805
|
includeUntracked: input.includeUntracked,
|
|
149741
149806
|
nameOnly: input.nameOnly,
|
|
149742
149807
|
stat: input.stat,
|
|
149743
|
-
unified: input.contextLines
|
|
149808
|
+
unified: input.contextLines,
|
|
149809
|
+
...input.autoExclude && {
|
|
149810
|
+
excludePatterns: [...AUTO_EXCLUDE_PATTERNS]
|
|
149811
|
+
}
|
|
149744
149812
|
}, {
|
|
149745
149813
|
workingDirectory: targetPath,
|
|
149746
149814
|
requestContext: appContext,
|
|
@@ -149751,7 +149819,10 @@ async function gitDiffLogic(input, { provider, targetPath, appContext }) {
|
|
|
149751
149819
|
diff: result.diff,
|
|
149752
149820
|
filesChanged: result.filesChanged || 0,
|
|
149753
149821
|
insertions: result.insertions,
|
|
149754
|
-
deletions: result.deletions
|
|
149822
|
+
deletions: result.deletions,
|
|
149823
|
+
...result.excludedFiles?.length && {
|
|
149824
|
+
excludedFiles: result.excludedFiles
|
|
149825
|
+
}
|
|
149755
149826
|
};
|
|
149756
149827
|
}
|
|
149757
149828
|
function filterGitDiffOutput(result, level) {
|
|
@@ -149760,7 +149831,8 @@ function filterGitDiffOutput(result, level) {
|
|
|
149760
149831
|
success: result.success,
|
|
149761
149832
|
filesChanged: result.filesChanged,
|
|
149762
149833
|
insertions: result.insertions,
|
|
149763
|
-
deletions: result.deletions
|
|
149834
|
+
deletions: result.deletions,
|
|
149835
|
+
excludedFiles: result.excludedFiles
|
|
149764
149836
|
};
|
|
149765
149837
|
}
|
|
149766
149838
|
return result;
|
|
@@ -150885,7 +150957,7 @@ var InputSchema27 = exports_external.object({
|
|
|
150885
150957
|
mode: exports_external.enum(["list", "create", "delete"]).default("list").describe("The tag operation to perform."),
|
|
150886
150958
|
tagName: TagNameSchema.optional().describe("Tag name for create/delete operations."),
|
|
150887
150959
|
commit: CommitRefSchema.optional().describe("Commit to tag (default: HEAD for create operation)."),
|
|
150888
|
-
message: exports_external.string().optional().describe("Tag message (creates annotated tag)."),
|
|
150960
|
+
message: exports_external.string().optional().describe("Tag message (creates annotated tag). For release tags, summarize the notable changes."),
|
|
150889
150961
|
annotated: exports_external.boolean().default(false).describe("Create annotated tag. Automatically set to true when message is provided."),
|
|
150890
150962
|
sign: SignSchema,
|
|
150891
150963
|
forceUnsignedOnFailure: exports_external.boolean().default(false).describe("If GPG/SSH signing fails, retry the tag creation without signing instead of failing."),
|
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.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",
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
"rollup": "4.59.0"
|
|
73
73
|
},
|
|
74
74
|
"devDependencies": {
|
|
75
|
-
"@cloudflare/workers-types": "^4.
|
|
75
|
+
"@cloudflare/workers-types": "^4.20260307.1",
|
|
76
76
|
"@eslint/js": "^10.0.1",
|
|
77
77
|
"@hono/mcp": "^0.2.4",
|
|
78
78
|
"@hono/node-server": "1.19.11",
|
|
@@ -96,7 +96,7 @@
|
|
|
96
96
|
"bun-types": "^1.3.10",
|
|
97
97
|
"depcheck": "^1.4.7",
|
|
98
98
|
"dotenv": "^17.3.1",
|
|
99
|
-
"eslint": "^10.0.
|
|
99
|
+
"eslint": "^10.0.3",
|
|
100
100
|
"execa": "^9.6.1",
|
|
101
101
|
"globals": "^17.4.0",
|
|
102
102
|
"hono": "4.12.5",
|