@rethunk/mcp-multi-root-git 2.6.0 → 2.8.0
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/AGENTS.md +1 -1
- package/CHANGELOG.md +21 -0
- package/README.md +1 -1
- package/dist/server/batch-commit-tool.js +7 -6
- package/dist/server/error-codes.js +95 -0
- package/dist/server/git-blame-tool.js +227 -0
- package/dist/server/git-branch-list-tool.js +140 -0
- package/dist/server/git-cherry-pick-tool.js +11 -10
- package/dist/server/git-diff-summary-tool.js +3 -2
- package/dist/server/git-diff-tool.js +56 -12
- package/dist/server/git-fetch-tool.js +142 -14
- package/dist/server/git-inventory-tool.js +5 -4
- package/dist/server/git-log-tool.js +10 -5
- package/dist/server/git-merge-tool.js +15 -14
- package/dist/server/git-parity-tool.js +3 -2
- package/dist/server/git-push-tool.js +9 -5
- package/dist/server/git-reflog-tool.js +126 -0
- package/dist/server/git-reset-soft-tool.js +4 -3
- package/dist/server/git-show-tool.js +83 -23
- package/dist/server/git-stash-tool.js +2 -1
- package/dist/server/git-tag-tool.js +8 -7
- package/dist/server/git-worktree-tool.js +8 -7
- package/dist/server/git.js +70 -4
- package/dist/server/list-presets-tool.js +2 -1
- package/dist/server/presets-resource.js +3 -2
- package/dist/server/presets.js +11 -5
- package/dist/server/roots.js +11 -10
- package/dist/server/tool-parameter-schemas.js +9 -0
- package/dist/server/tools.js +6 -0
- package/docs/mcp-tools.md +119 -6
- package/package.json +2 -2
- package/schemas/git_blame.json +52 -0
- package/schemas/git_branch_list.json +36 -0
- package/schemas/git_diff.json +14 -1
- package/schemas/git_reflog.json +44 -0
- package/schemas/git_show.json +12 -1
- package/schemas/index.json +15 -0
- package/tool-parameters.schema.json +152 -2
package/dist/server/presets.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { z } from "zod";
|
|
4
|
+
import { ERROR_CODES } from "./error-codes.js";
|
|
4
5
|
/**
|
|
5
6
|
* Schema for `.rethunk/git-mcp-presets.json` at the workspace root.
|
|
6
7
|
* Each named entry defines roots for `git_inventory` and/or pairs for `git_parity`.
|
|
@@ -85,16 +86,21 @@ export function presetLoadErrorPayload(gitTop, fail) {
|
|
|
85
86
|
const presetFile = join(gitTop, PRESET_FILE_PATH);
|
|
86
87
|
if (fail.reason === "invalid_json") {
|
|
87
88
|
return {
|
|
88
|
-
error:
|
|
89
|
+
error: ERROR_CODES.PRESET_FILE_INVALID,
|
|
89
90
|
kind: "invalid_json",
|
|
90
91
|
presetFile,
|
|
91
92
|
message: fail.message,
|
|
92
93
|
};
|
|
93
94
|
}
|
|
94
95
|
if (fail.reason === "schema") {
|
|
95
|
-
return {
|
|
96
|
+
return {
|
|
97
|
+
error: ERROR_CODES.PRESET_FILE_INVALID,
|
|
98
|
+
kind: "schema",
|
|
99
|
+
presetFile,
|
|
100
|
+
issues: fail.issues,
|
|
101
|
+
};
|
|
96
102
|
}
|
|
97
|
-
return { error:
|
|
103
|
+
return { error: ERROR_CODES.PRESET_FILE_INVALID, presetFile };
|
|
98
104
|
}
|
|
99
105
|
function getPresetEntry(gitTop, presetName) {
|
|
100
106
|
const loaded = loadPresetsFromGitTop(gitTop);
|
|
@@ -103,7 +109,7 @@ function getPresetEntry(gitTop, presetName) {
|
|
|
103
109
|
return {
|
|
104
110
|
ok: false,
|
|
105
111
|
error: {
|
|
106
|
-
error:
|
|
112
|
+
error: ERROR_CODES.PRESET_NOT_FOUND,
|
|
107
113
|
preset: presetName,
|
|
108
114
|
presetFile: join(gitTop, PRESET_FILE_PATH),
|
|
109
115
|
},
|
|
@@ -116,7 +122,7 @@ function getPresetEntry(gitTop, presetName) {
|
|
|
116
122
|
return {
|
|
117
123
|
ok: false,
|
|
118
124
|
error: {
|
|
119
|
-
error:
|
|
125
|
+
error: ERROR_CODES.PRESET_NOT_FOUND,
|
|
120
126
|
preset: presetName,
|
|
121
127
|
presetFile: join(gitTop, PRESET_FILE_PATH),
|
|
122
128
|
},
|
package/dist/server/roots.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { basename, resolve } from "node:path";
|
|
2
2
|
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { ERROR_CODES } from "./error-codes.js";
|
|
3
4
|
import { gateGit, gitTopLevel } from "./git.js";
|
|
4
5
|
import { loadPresetsFromGitTop, presetLoadErrorPayload } from "./presets.js";
|
|
5
6
|
import { MAX_ABSOLUTE_GIT_ROOTS } from "./schemas.js";
|
|
@@ -61,7 +62,7 @@ export function resolveAbsoluteGitRootsList(raw) {
|
|
|
61
62
|
return {
|
|
62
63
|
ok: false,
|
|
63
64
|
error: {
|
|
64
|
-
error:
|
|
65
|
+
error: ERROR_CODES.ABSOLUTE_GIT_ROOTS_TOO_MANY,
|
|
65
66
|
max: MAX_ABSOLUTE_GIT_ROOTS,
|
|
66
67
|
count: raw.length,
|
|
67
68
|
},
|
|
@@ -72,12 +73,12 @@ export function resolveAbsoluteGitRootsList(raw) {
|
|
|
72
73
|
for (const item of raw) {
|
|
73
74
|
const trimmed = item.trim();
|
|
74
75
|
if (trimmed.length === 0) {
|
|
75
|
-
return { ok: false, error: { error:
|
|
76
|
+
return { ok: false, error: { error: ERROR_CODES.INVALID_ABSOLUTE_GIT_ROOT, path: item } };
|
|
76
77
|
}
|
|
77
78
|
const abs = resolve(trimmed);
|
|
78
79
|
const top = gitTopLevel(abs);
|
|
79
80
|
if (!top) {
|
|
80
|
-
return { ok: false, error: { error:
|
|
81
|
+
return { ok: false, error: { error: ERROR_CODES.INVALID_ABSOLUTE_GIT_ROOT, path: abs } };
|
|
81
82
|
}
|
|
82
83
|
if (seen.has(top))
|
|
83
84
|
continue;
|
|
@@ -85,7 +86,7 @@ export function resolveAbsoluteGitRootsList(raw) {
|
|
|
85
86
|
tops.push(top);
|
|
86
87
|
}
|
|
87
88
|
if (tops.length === 0) {
|
|
88
|
-
return { ok: false, error: { error:
|
|
89
|
+
return { ok: false, error: { error: ERROR_CODES.ABSOLUTE_GIT_ROOTS_EMPTY } };
|
|
89
90
|
}
|
|
90
91
|
return { ok: true, roots: tops };
|
|
91
92
|
}
|
|
@@ -104,7 +105,7 @@ function resolveWorkspaceRoots(server, args) {
|
|
|
104
105
|
return {
|
|
105
106
|
ok: false,
|
|
106
107
|
error: {
|
|
107
|
-
error:
|
|
108
|
+
error: ERROR_CODES.ROOT_INDEX_OUT_OF_RANGE,
|
|
108
109
|
rootIndex: args.rootIndex,
|
|
109
110
|
rootCount: fileRoots.length,
|
|
110
111
|
},
|
|
@@ -163,10 +164,10 @@ export function requireGitAndRoots(server, args, presetName) {
|
|
|
163
164
|
const abs = args.absoluteGitRoots;
|
|
164
165
|
if (abs != null && abs.length > 0) {
|
|
165
166
|
if (presetName) {
|
|
166
|
-
return { ok: false, error: { error:
|
|
167
|
+
return { ok: false, error: { error: ERROR_CODES.ABSOLUTE_GIT_ROOTS_PRESET_CONFLICT } };
|
|
167
168
|
}
|
|
168
169
|
if (hasExclusiveWorkspacePick(args)) {
|
|
169
|
-
return { ok: false, error: { error:
|
|
170
|
+
return { ok: false, error: { error: ERROR_CODES.ABSOLUTE_GIT_ROOTS_EXCLUSIVE } };
|
|
170
171
|
}
|
|
171
172
|
return resolveAbsoluteGitRootsList(abs);
|
|
172
173
|
}
|
|
@@ -191,16 +192,16 @@ export function requireSingleRepo(server, args, presetName = undefined) {
|
|
|
191
192
|
return {
|
|
192
193
|
ok: false,
|
|
193
194
|
error: {
|
|
194
|
-
error:
|
|
195
|
+
error: ERROR_CODES.ABSOLUTE_GIT_ROOTS_SINGLE_REPO_ONLY,
|
|
195
196
|
rootCount: pre.roots.length,
|
|
196
197
|
},
|
|
197
198
|
};
|
|
198
199
|
}
|
|
199
200
|
const rootInput = pre.roots[0];
|
|
200
201
|
if (!rootInput)
|
|
201
|
-
return { ok: false, error: { error:
|
|
202
|
+
return { ok: false, error: { error: ERROR_CODES.NO_WORKSPACE_ROOT } };
|
|
202
203
|
const top = gitTopLevel(rootInput);
|
|
203
204
|
if (!top)
|
|
204
|
-
return { ok: false, error: { error:
|
|
205
|
+
return { ok: false, error: { error: ERROR_CODES.NOT_A_GIT_REPOSITORY, path: rootInput } };
|
|
205
206
|
return { ok: true, gitTop: top };
|
|
206
207
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { registerBatchCommitTool } from "./batch-commit-tool.js";
|
|
3
|
+
import { registerGitBlameTool } from "./git-blame-tool.js";
|
|
4
|
+
import { registerGitBranchListTool } from "./git-branch-list-tool.js";
|
|
3
5
|
import { registerGitCherryPickTool } from "./git-cherry-pick-tool.js";
|
|
4
6
|
import { registerGitDiffSummaryTool } from "./git-diff-summary-tool.js";
|
|
5
7
|
import { registerGitDiffTool } from "./git-diff-tool.js";
|
|
@@ -9,6 +11,7 @@ import { registerGitLogTool } from "./git-log-tool.js";
|
|
|
9
11
|
import { registerGitMergeTool } from "./git-merge-tool.js";
|
|
10
12
|
import { registerGitParityTool } from "./git-parity-tool.js";
|
|
11
13
|
import { registerGitPushTool } from "./git-push-tool.js";
|
|
14
|
+
import { registerGitReflogTool } from "./git-reflog-tool.js";
|
|
12
15
|
import { registerGitResetSoftTool } from "./git-reset-soft-tool.js";
|
|
13
16
|
import { registerGitShowTool } from "./git-show-tool.js";
|
|
14
17
|
import { registerGitStashApplyTool, registerGitStashListTool } from "./git-stash-tool.js";
|
|
@@ -29,6 +32,9 @@ export const READ_ONLY_SINGLE_REPO_TOOLS = [
|
|
|
29
32
|
"git_show",
|
|
30
33
|
"git_worktree_list",
|
|
31
34
|
"git_stash_list",
|
|
35
|
+
"git_blame",
|
|
36
|
+
"git_branch_list",
|
|
37
|
+
"git_reflog",
|
|
32
38
|
];
|
|
33
39
|
export const MUTATING_TOOLS = [
|
|
34
40
|
"git_fetch",
|
|
@@ -71,6 +77,9 @@ export function captureToolParameterSchemas() {
|
|
|
71
77
|
registerGitWorktreeListTool(server);
|
|
72
78
|
registerGitStashListTool(server);
|
|
73
79
|
registerGitFetchTool(server);
|
|
80
|
+
registerGitBlameTool(server);
|
|
81
|
+
registerGitBranchListTool(server);
|
|
82
|
+
registerGitReflogTool(server);
|
|
74
83
|
registerBatchCommitTool(server);
|
|
75
84
|
registerGitPushTool(server);
|
|
76
85
|
registerGitMergeTool(server);
|
package/dist/server/tools.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { registerBatchCommitTool } from "./batch-commit-tool.js";
|
|
2
|
+
import { registerGitBlameTool } from "./git-blame-tool.js";
|
|
3
|
+
import { registerGitBranchListTool } from "./git-branch-list-tool.js";
|
|
2
4
|
import { registerGitCherryPickTool } from "./git-cherry-pick-tool.js";
|
|
3
5
|
import { registerGitDiffSummaryTool } from "./git-diff-summary-tool.js";
|
|
4
6
|
import { registerGitDiffTool } from "./git-diff-tool.js";
|
|
@@ -8,6 +10,7 @@ import { registerGitLogTool } from "./git-log-tool.js";
|
|
|
8
10
|
import { registerGitMergeTool } from "./git-merge-tool.js";
|
|
9
11
|
import { registerGitParityTool } from "./git-parity-tool.js";
|
|
10
12
|
import { registerGitPushTool } from "./git-push-tool.js";
|
|
13
|
+
import { registerGitReflogTool } from "./git-reflog-tool.js";
|
|
11
14
|
import { registerGitResetSoftTool } from "./git-reset-soft-tool.js";
|
|
12
15
|
import { registerGitShowTool } from "./git-show-tool.js";
|
|
13
16
|
import { registerGitStashApplyTool, registerGitStashListTool } from "./git-stash-tool.js";
|
|
@@ -29,6 +32,9 @@ export function registerRethunkGitTools(server) {
|
|
|
29
32
|
registerGitWorktreeListTool(server);
|
|
30
33
|
registerGitStashListTool(server);
|
|
31
34
|
registerGitFetchTool(server);
|
|
35
|
+
registerGitBlameTool(server);
|
|
36
|
+
registerGitBranchListTool(server);
|
|
37
|
+
registerGitReflogTool(server);
|
|
32
38
|
// Mutating tools
|
|
33
39
|
registerBatchCommitTool(server);
|
|
34
40
|
registerGitPushTool(server);
|
package/docs/mcp-tools.md
CHANGED
|
@@ -17,11 +17,14 @@ MCP clients expose tools as `{serverName}_{toolName}`. With the server registere
|
|
|
17
17
|
| `list_presets` | `rethunk-git_list_presets` | List preset names/counts from `.rethunk/git-mcp-presets.json`; invalid JSON/schema surface as errors. Workspace pick + `format` only (includes `absoluteGitRoots`). **Read-only.** |
|
|
18
18
|
| `git_log` | `rethunk-git_git_log` | Path-filtered, time-windowed `git log` across one or more workspace roots. Returns commit history with author, date, subject, and shortstat. Args: `since`, `paths`, `grep`, `author`, `maxCommits`, `branch`, plus workspace pick args (`absoluteGitRoots` for sibling clones) + `format` (`markdown`/`json`/`oneline`). **Read-only.** |
|
|
19
19
|
| `git_diff_summary` | `rethunk-git_git_diff_summary` | Structured, token-efficient diff viewer. Returns per-file diffs with additions/deletions counts, truncated to configurable line limits, with lock files/dist/vendor excluded by default. Args: `range`, `fileFilter`, `maxLinesPerFile`, `maxFiles`, `excludePatterns`, plus workspace pick args (optional single-entry `absoluteGitRoots`) + `format`. **Read-only.** |
|
|
20
|
-
| `git_diff` | `rethunk-git_git_diff` | Raw diff text for a single repo. Supports unstaged, staged, or `base..head` ranges,
|
|
21
|
-
| `git_show` | `rethunk-git_git_show` | Inspect one commit or ref. Returns commit message plus diff, or file content at `path` for that ref. Args: `ref`, `path?`, plus single-repo workspace pick + `format`. **Read-only.** |
|
|
20
|
+
| `git_diff` | `rethunk-git_git_diff` | Raw diff text for a single repo. Supports unstaged, staged, or `base..head` ranges, scoped to one or more paths with configurable context width. Args: `workspaceRoot`, `rootIndex`, `format`, `base?`, `head?`, `path?`, `paths?`, `unified?`, `staged?`. No `absoluteGitRoots` or `allWorkspaceRoots`. **Read-only.** |
|
|
21
|
+
| `git_show` | `rethunk-git_git_show` | Inspect one commit or ref. Returns commit message plus diff (or `--stat` diffstat), or file content at `path` for that ref. Args: `ref`, `path?`, `paths?`, `stat?`, plus single-repo workspace pick + `format`. **Read-only.** |
|
|
22
22
|
| `git_worktree_list` | `rethunk-git_git_worktree_list` | List all worktrees (`git worktree list --porcelain`). Workspace pick + `format`. **Read-only.** |
|
|
23
23
|
| `git_stash_list` | `rethunk-git_git_stash_list` | List `git stash` entries for one repo. Args: single-repo workspace pick + `format`. **Read-only.** |
|
|
24
|
-
| `
|
|
24
|
+
| `git_blame` | `rethunk-git_git_blame` | Line-by-line authorship for a file: commit SHA, author, date, summary per line. Args: `path` (required), `ref?`, `startLine?`, `endLine?`, plus single-repo workspace pick + `format`. **Read-only.** |
|
|
25
|
+
| `git_branch_list` | `rethunk-git_git_branch_list` | List local branches (sha, current marker, upstream); optional `includeRemotes` adds remote-tracking branches. Args: `includeRemotes?`, plus single-repo workspace pick + `format`. **Read-only.** |
|
|
26
|
+
| `git_reflog` | `rethunk-git_git_reflog` | Show the reflog for a ref (default `HEAD`) — recent HEAD movements with selector, SHA, and message. Args: `ref?`, `maxEntries?`, plus single-repo workspace pick + `format`. **Read-only.** |
|
|
27
|
+
| `git_fetch` | `rethunk-git_git_fetch` | Fetch from a remote without modifying the working tree. Updates refs only and reports updated/new refs, plus structured `updated`/`created`/`pruned` deltas on git ≥ 2.41. Args: `remote?`, `branch?`, `prune?`, `tags?`, plus single-repo workspace pick + `format`. **Mutating — refs only.** |
|
|
25
28
|
| `git_push` | `rethunk-git_git_push` | Push the current branch to its upstream. Optional `remote`, `branch`, `setUpstream` (passes `-u`). Refuses on detached HEAD; never force-pushes. Workspace pick + `format`. **Mutating.** |
|
|
26
29
|
| `git_tag` | `rethunk-git_git_tag` | Create/delete annotated or lightweight tags for one repo. Args: `tag`, `message?`, `ref?`, `delete?`, plus single-repo workspace pick + `format`. **Mutating.** |
|
|
27
30
|
| `git_worktree_add` | `rethunk-git_git_worktree_add` | Create a new linked worktree, creating the branch from `baseRef` if it does not yet exist. Refuses on protected branch names. Args: `path`, `branch`, `baseRef?`, plus workspace pick + `format`. **Mutating.** |
|
|
@@ -415,7 +418,9 @@ The response contains one **`parity[]`** entry per resolved git toplevel. `absol
|
|
|
415
418
|
|-----------|------|---------|-------|
|
|
416
419
|
| `base` | string | — | Base ref for a revision diff. When omitted with no `staged`, the tool shows unstaged changes. |
|
|
417
420
|
| `head` | string | `HEAD` | Head ref for a revision diff. Used only when `base` is provided. |
|
|
418
|
-
| `path` | string | — | Optional single file path to scope the diff. |
|
|
421
|
+
| `path` | string | — | Optional single file path to scope the diff. Confined to the repo (`path_escapes_repo` on escape). |
|
|
422
|
+
| `paths` | string[] | — | Multiple file paths to scope the diff; unioned with `path` (deduped). Each confined to the repo. |
|
|
423
|
+
| `unified` | integer | — | Context lines around each change (passed as `-U<n>`, 0–100). Omit for git's default (3). |
|
|
419
424
|
| `staged` | boolean | `false` | When `true`, runs `git diff --staged`. Ignored when `base` is provided. |
|
|
420
425
|
| `workspaceRoot` | string | — | Explicit root; highest priority. |
|
|
421
426
|
| `rootIndex` | int | — | Pick one of several MCP roots (0-based). |
|
|
@@ -437,6 +442,7 @@ The response contains one **`parity[]`** entry per resolved git toplevel. `absol
|
|
|
437
442
|
| Code | Meaning |
|
|
438
443
|
|------|---------|
|
|
439
444
|
| `unsafe_range_token` | `base` or `head` contains characters outside the argv-safe subset. |
|
|
445
|
+
| `path_escapes_repo` | A `path` / `paths` entry resolves outside the git toplevel. |
|
|
440
446
|
| `git_diff_failed` | `git diff` exited non-zero. |
|
|
441
447
|
| `not_a_git_repository` | The resolved workspace root is not inside a git repository. |
|
|
442
448
|
|
|
@@ -448,6 +454,8 @@ The response contains one **`parity[]`** entry per resolved git toplevel. `absol
|
|
|
448
454
|
|-----------|------|-------|
|
|
449
455
|
| `ref` | string | Commit, branch, tag, or other git rev-spec to inspect. |
|
|
450
456
|
| `path` | string | Optional single path. When provided, the response shows that path's content at `ref` instead of the full commit diff. |
|
|
457
|
+
| `stat` | boolean | When `true`, runs `git show --stat` — commit message plus per-file diffstat, no full patch (`statOutput` in JSON). |
|
|
458
|
+
| `paths` | string[] | Filter the shown patch/stat to these repo-relative paths; unioned with `path`. Each confined to the repo. |
|
|
451
459
|
| `workspaceRoot`, `rootIndex`, `format` | — | Standard single-repo workspace pick + output format. |
|
|
452
460
|
|
|
453
461
|
### `git_show` — JSON shape (`format: "json"`)
|
|
@@ -461,7 +469,7 @@ The response contains one **`parity[]`** entry per resolved git toplevel. `absol
|
|
|
461
469
|
}
|
|
462
470
|
```
|
|
463
471
|
|
|
464
|
-
`path` is omitted when not requested. `diff` is omitted when `git show` returns only a commit message.
|
|
472
|
+
`path` is omitted when not requested; `paths` is present when multiple paths were given. `diff` is omitted when `git show` returns only a commit message. With `stat: true`, `stat` is `true` and `statOutput` carries the diffstat instead of a full `diff`.
|
|
465
473
|
|
|
466
474
|
### `git_show` — error codes
|
|
467
475
|
|
|
@@ -517,11 +525,14 @@ The response contains one **`parity[]`** entry per resolved git toplevel. `absol
|
|
|
517
525
|
"remote": "origin",
|
|
518
526
|
"updatedRefs": ["abc1234..def5678 main -> origin/main"],
|
|
519
527
|
"newRefs": ["[new tag] v2.0.0 -> v2.0.0"],
|
|
528
|
+
"updated": [{ "ref": "refs/remotes/origin/main", "oldSha": "abc1234…", "newSha": "def5678…", "flag": " " }],
|
|
529
|
+
"created": [{ "ref": "refs/tags/v2.0.0", "newSha": "0a1b2c3…", "flag": "*" }],
|
|
530
|
+
"pruned": [{ "ref": "refs/remotes/origin/old" }],
|
|
520
531
|
"output": "From origin\n..."
|
|
521
532
|
}
|
|
522
533
|
```
|
|
523
534
|
|
|
524
|
-
Fetch failures are reported as `ok: false` with the captured git output in `output`.
|
|
535
|
+
`updated` / `created` / `pruned` are structured ref deltas parsed from `git fetch --porcelain` (git ≥ 2.41), each omitted when empty. On older git the `--porcelain` option is detected as unsupported and the tool falls back to a plain fetch, omitting the structured arrays; the `updatedRefs` / `newRefs` string fields are always present for back-compat. Fetch failures are reported as `ok: false` with the captured git output in `output`.
|
|
525
536
|
|
|
526
537
|
### `git_fetch` — error codes
|
|
527
538
|
|
|
@@ -533,6 +544,101 @@ Fetch failures are reported as `ok: false` with the captured git output in `outp
|
|
|
533
544
|
|
|
534
545
|
---
|
|
535
546
|
|
|
547
|
+
### `git_blame` — parameters
|
|
548
|
+
|
|
549
|
+
| Parameter | Type | Default | Notes |
|
|
550
|
+
|-----------|------|---------|-------|
|
|
551
|
+
| `path` | string | — | **Required.** Repo-relative file to annotate. Confined to the repo (`path_escapes_repo` on escape). |
|
|
552
|
+
| `ref` | string | working tree | Commit-ish to blame at. Validated as a safe ref token. |
|
|
553
|
+
| `startLine` | int | — | Start of a line range (`-L`). Requires `endLine`. |
|
|
554
|
+
| `endLine` | int | — | End of the line range, inclusive. Requires `startLine`. |
|
|
555
|
+
| `workspaceRoot`, `rootIndex`, `format` | — | Standard single-repo workspace pick + output format. |
|
|
556
|
+
|
|
557
|
+
### `git_blame` — JSON shape (`format: "json"`)
|
|
558
|
+
|
|
559
|
+
```json
|
|
560
|
+
{
|
|
561
|
+
"ref": "HEAD",
|
|
562
|
+
"path": "src/server.ts",
|
|
563
|
+
"lines": [
|
|
564
|
+
{ "line": 1, "sha": "a1b2c3d4…", "author": "Damon Blais", "date": "2026-04-12T18:32:01-07:00", "summary": "feat: add tool", "content": "import { FastMCP } from \"fastmcp\";" }
|
|
565
|
+
]
|
|
566
|
+
}
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
`ref` is omitted when blaming the working tree.
|
|
570
|
+
|
|
571
|
+
### `git_blame` — error codes
|
|
572
|
+
|
|
573
|
+
| Code | Meaning |
|
|
574
|
+
|------|---------|
|
|
575
|
+
| `path_escapes_repo` | `path` resolves outside the git toplevel. |
|
|
576
|
+
| `unsafe_ref_token` | `ref` contains characters outside the argv-safe subset. |
|
|
577
|
+
| `invalid_line_range` | Only one of `startLine`/`endLine` was given, or `startLine > endLine`. |
|
|
578
|
+
| `git_blame_failed` | `git blame` exited non-zero (unknown path/ref). `detail` carries stderr. |
|
|
579
|
+
| `not_a_git_repository` | The resolved workspace root is not inside a git repository. |
|
|
580
|
+
|
|
581
|
+
---
|
|
582
|
+
|
|
583
|
+
### `git_branch_list` — parameters
|
|
584
|
+
|
|
585
|
+
| Parameter | Type | Default | Notes |
|
|
586
|
+
|-----------|------|---------|-------|
|
|
587
|
+
| `includeRemotes` | boolean | `false` | Also list remote-tracking branches (`refs/remotes`); symbolic `origin/HEAD` is skipped. |
|
|
588
|
+
| `workspaceRoot`, `rootIndex`, `format` | — | Standard single-repo workspace pick + output format. |
|
|
589
|
+
|
|
590
|
+
### `git_branch_list` — JSON shape (`format: "json"`)
|
|
591
|
+
|
|
592
|
+
```json
|
|
593
|
+
{
|
|
594
|
+
"branches": [
|
|
595
|
+
{ "name": "main", "sha": "a1b2c3d4…", "current": true, "upstream": "origin/main" },
|
|
596
|
+
{ "name": "feature/x", "sha": "b2c3d4e5…", "current": false }
|
|
597
|
+
],
|
|
598
|
+
"remotes": [{ "name": "origin/main", "sha": "a1b2c3d4…" }]
|
|
599
|
+
}
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
`upstream` is omitted when a branch has no upstream. `remotes` is present only when `includeRemotes: true`.
|
|
603
|
+
|
|
604
|
+
### `git_branch_list` — error codes
|
|
605
|
+
|
|
606
|
+
| Code | Meaning |
|
|
607
|
+
|------|---------|
|
|
608
|
+
| `branch_list_failed` | `git for-each-ref` exited non-zero. `detail` carries stderr. |
|
|
609
|
+
| `not_a_git_repository` | The resolved workspace root is not inside a git repository. |
|
|
610
|
+
|
|
611
|
+
---
|
|
612
|
+
|
|
613
|
+
### `git_reflog` — parameters
|
|
614
|
+
|
|
615
|
+
| Parameter | Type | Default | Notes |
|
|
616
|
+
|-----------|------|---------|-------|
|
|
617
|
+
| `ref` | string | `HEAD` | Ref whose reflog to show. Validated as a safe ref token. |
|
|
618
|
+
| `maxEntries` | int | `30` | Max entries to return (1–200). |
|
|
619
|
+
| `workspaceRoot`, `rootIndex`, `format` | — | Standard single-repo workspace pick + output format. |
|
|
620
|
+
|
|
621
|
+
### `git_reflog` — JSON shape (`format: "json"`)
|
|
622
|
+
|
|
623
|
+
```json
|
|
624
|
+
{
|
|
625
|
+
"ref": "HEAD",
|
|
626
|
+
"entries": [
|
|
627
|
+
{ "sha": "a1b2c3d4…", "selector": "HEAD@{0}", "message": "commit: feat: add tool" }
|
|
628
|
+
]
|
|
629
|
+
}
|
|
630
|
+
```
|
|
631
|
+
|
|
632
|
+
### `git_reflog` — error codes
|
|
633
|
+
|
|
634
|
+
| Code | Meaning |
|
|
635
|
+
|------|---------|
|
|
636
|
+
| `unsafe_ref_token` | `ref` contains characters outside the argv-safe subset. |
|
|
637
|
+
| `reflog_failed` | `git reflog show` exited non-zero. `detail` carries stderr. |
|
|
638
|
+
| `not_a_git_repository` | The resolved workspace root is not inside a git repository. |
|
|
639
|
+
|
|
640
|
+
---
|
|
641
|
+
|
|
536
642
|
### `batch_commit` — atomic staging semantics
|
|
537
643
|
|
|
538
644
|
**Critical for AI agents:** Each call to `batch_commit` is **self-contained and atomic per-commit entry**.
|
|
@@ -917,6 +1023,13 @@ For deletions, `type` is `"deleted"` and `sha` is an empty string.
|
|
|
917
1023
|
|
|
918
1024
|
---
|
|
919
1025
|
|
|
1026
|
+
## Environment
|
|
1027
|
+
|
|
1028
|
+
| Variable | Default | Notes |
|
|
1029
|
+
|----------|---------|-------|
|
|
1030
|
+
| `GIT_SUBPROCESS_PARALLELISM` | CPU-based | Max concurrent git subprocesses for multi-root fan-out (`git_inventory`, `git_parity`, multi-root `git_log`). |
|
|
1031
|
+
| `GIT_SUBPROCESS_TIMEOUT_MS` | `120000` | Per-subprocess timeout in ms; on expiry the child is killed (SIGTERM) and the call resolves as failed. Set `0` (or negative) to disable (unbounded). |
|
|
1032
|
+
|
|
920
1033
|
## Resource
|
|
921
1034
|
|
|
922
1035
|
| URI | Purpose |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rethunk/mcp-multi-root-git",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.8.0",
|
|
4
4
|
"description": "MCP stdio server: multi-root git status, inventory, and HEAD parity checks. Generic tools usable by any workspace.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"zod": "^4.4.3"
|
|
70
70
|
},
|
|
71
71
|
"devDependencies": {
|
|
72
|
-
"@biomejs/biome": "^2.4.
|
|
72
|
+
"@biomejs/biome": "^2.4.16",
|
|
73
73
|
"@types/node": "^25.9.1",
|
|
74
74
|
"rimraf": "^6.1.3",
|
|
75
75
|
"typescript": "^6.0.3"
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"title": "@rethunk/mcp-multi-root-git: git_blame",
|
|
4
|
+
"description": "Parameter schema for the 'git_blame' MCP tool.",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"properties": {
|
|
7
|
+
"workspaceRoot": {
|
|
8
|
+
"description": "Highest-priority override.",
|
|
9
|
+
"type": "string"
|
|
10
|
+
},
|
|
11
|
+
"rootIndex": {
|
|
12
|
+
"description": "0-based index into the MCP file roots list; ignored when workspaceRoot is set.",
|
|
13
|
+
"type": "integer",
|
|
14
|
+
"minimum": 0,
|
|
15
|
+
"maximum": 9007199254740991
|
|
16
|
+
},
|
|
17
|
+
"format": {
|
|
18
|
+
"default": "markdown",
|
|
19
|
+
"type": "string",
|
|
20
|
+
"enum": [
|
|
21
|
+
"markdown",
|
|
22
|
+
"json"
|
|
23
|
+
]
|
|
24
|
+
},
|
|
25
|
+
"path": {
|
|
26
|
+
"type": "string",
|
|
27
|
+
"minLength": 1,
|
|
28
|
+
"description": "Repo-relative path to the file to blame."
|
|
29
|
+
},
|
|
30
|
+
"ref": {
|
|
31
|
+
"description": "Optional commit-ish (SHA, branch, tag) to blame at.",
|
|
32
|
+
"type": "string"
|
|
33
|
+
},
|
|
34
|
+
"startLine": {
|
|
35
|
+
"description": "First line of the range to blame (1-based). Requires endLine.",
|
|
36
|
+
"type": "integer",
|
|
37
|
+
"minimum": 1,
|
|
38
|
+
"maximum": 9007199254740991
|
|
39
|
+
},
|
|
40
|
+
"endLine": {
|
|
41
|
+
"description": "Last line of the range to blame (1-based, inclusive). Requires startLine.",
|
|
42
|
+
"type": "integer",
|
|
43
|
+
"minimum": 1,
|
|
44
|
+
"maximum": 9007199254740991
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"required": [
|
|
48
|
+
"format",
|
|
49
|
+
"path"
|
|
50
|
+
],
|
|
51
|
+
"additionalProperties": false
|
|
52
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"title": "@rethunk/mcp-multi-root-git: git_branch_list",
|
|
4
|
+
"description": "Parameter schema for the 'git_branch_list' MCP tool.",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"properties": {
|
|
7
|
+
"workspaceRoot": {
|
|
8
|
+
"description": "Highest-priority override.",
|
|
9
|
+
"type": "string"
|
|
10
|
+
},
|
|
11
|
+
"rootIndex": {
|
|
12
|
+
"description": "0-based index into the MCP file roots list; ignored when workspaceRoot is set.",
|
|
13
|
+
"type": "integer",
|
|
14
|
+
"minimum": 0,
|
|
15
|
+
"maximum": 9007199254740991
|
|
16
|
+
},
|
|
17
|
+
"format": {
|
|
18
|
+
"default": "markdown",
|
|
19
|
+
"type": "string",
|
|
20
|
+
"enum": [
|
|
21
|
+
"markdown",
|
|
22
|
+
"json"
|
|
23
|
+
]
|
|
24
|
+
},
|
|
25
|
+
"includeRemotes": {
|
|
26
|
+
"default": false,
|
|
27
|
+
"description": "When true, also return remote-tracking branches from refs/remotes (excluding symbolic origin/HEAD).",
|
|
28
|
+
"type": "boolean"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"required": [
|
|
32
|
+
"format",
|
|
33
|
+
"includeRemotes"
|
|
34
|
+
],
|
|
35
|
+
"additionalProperties": false
|
|
36
|
+
}
|
package/schemas/git_diff.json
CHANGED
|
@@ -31,13 +31,26 @@
|
|
|
31
31
|
"type": "string"
|
|
32
32
|
},
|
|
33
33
|
"path": {
|
|
34
|
-
"description": "Scope diff to a single file path (e.g. \"src/main.ts\").",
|
|
34
|
+
"description": "Scope diff to a single file path (e.g. \"src/main.ts\"). For multiple files, prefer `paths`. If both `path` and `paths` are given, they are unioned.",
|
|
35
35
|
"type": "string"
|
|
36
36
|
},
|
|
37
|
+
"paths": {
|
|
38
|
+
"description": "Scope diff to multiple file paths (e.g. [\"src/a.ts\", \"src/b.ts\"]). Each path is validated and must lie within the repository root. If both `path` and `paths` are given, they are unioned.",
|
|
39
|
+
"type": "array",
|
|
40
|
+
"items": {
|
|
41
|
+
"type": "string"
|
|
42
|
+
}
|
|
43
|
+
},
|
|
37
44
|
"staged": {
|
|
38
45
|
"default": false,
|
|
39
46
|
"description": "If true, show staged changes (git diff --staged). Ignored if `base` is provided.",
|
|
40
47
|
"type": "boolean"
|
|
48
|
+
},
|
|
49
|
+
"unified": {
|
|
50
|
+
"description": "Number of context lines to show around each change (passed as -U<n> to git diff). Defaults to git's built-in default (3). Use 0 for no context.",
|
|
51
|
+
"type": "integer",
|
|
52
|
+
"minimum": 0,
|
|
53
|
+
"maximum": 100
|
|
41
54
|
}
|
|
42
55
|
},
|
|
43
56
|
"required": [
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"title": "@rethunk/mcp-multi-root-git: git_reflog",
|
|
4
|
+
"description": "Parameter schema for the 'git_reflog' MCP tool.",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"properties": {
|
|
7
|
+
"workspaceRoot": {
|
|
8
|
+
"description": "Highest-priority override.",
|
|
9
|
+
"type": "string"
|
|
10
|
+
},
|
|
11
|
+
"rootIndex": {
|
|
12
|
+
"description": "0-based index into the MCP file roots list; ignored when workspaceRoot is set.",
|
|
13
|
+
"type": "integer",
|
|
14
|
+
"minimum": 0,
|
|
15
|
+
"maximum": 9007199254740991
|
|
16
|
+
},
|
|
17
|
+
"format": {
|
|
18
|
+
"default": "markdown",
|
|
19
|
+
"type": "string",
|
|
20
|
+
"enum": [
|
|
21
|
+
"markdown",
|
|
22
|
+
"json"
|
|
23
|
+
]
|
|
24
|
+
},
|
|
25
|
+
"ref": {
|
|
26
|
+
"default": "HEAD",
|
|
27
|
+
"description": "Ref whose reflog to show (branch name, HEAD, etc.). Default: HEAD.",
|
|
28
|
+
"type": "string"
|
|
29
|
+
},
|
|
30
|
+
"maxEntries": {
|
|
31
|
+
"default": 30,
|
|
32
|
+
"description": "Maximum reflog entries to return (hard cap 200). Default 30.",
|
|
33
|
+
"type": "integer",
|
|
34
|
+
"minimum": 1,
|
|
35
|
+
"maximum": 200
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"required": [
|
|
39
|
+
"format",
|
|
40
|
+
"ref",
|
|
41
|
+
"maxEntries"
|
|
42
|
+
],
|
|
43
|
+
"additionalProperties": false
|
|
44
|
+
}
|
package/schemas/git_show.json
CHANGED
|
@@ -28,8 +28,19 @@
|
|
|
28
28
|
"description": "Commit reference (SHA, branch, tag, or any git rev-spec)."
|
|
29
29
|
},
|
|
30
30
|
"path": {
|
|
31
|
-
"description": "Optional file path to inspect at the ref.
|
|
31
|
+
"description": "Optional single file path to inspect at the ref. Merged with `paths` when both are provided.",
|
|
32
32
|
"type": "string"
|
|
33
|
+
},
|
|
34
|
+
"paths": {
|
|
35
|
+
"description": "Optional list of file paths to filter the shown diff/stat. Merged with `path` when both are provided.",
|
|
36
|
+
"type": "array",
|
|
37
|
+
"items": {
|
|
38
|
+
"type": "string"
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"stat": {
|
|
42
|
+
"description": "When true, show --stat diffstat (files changed summary) instead of the full patch.",
|
|
43
|
+
"type": "boolean"
|
|
33
44
|
}
|
|
34
45
|
},
|
|
35
46
|
"required": [
|
package/schemas/index.json
CHANGED
|
@@ -54,6 +54,21 @@
|
|
|
54
54
|
"file": "git_stash_list.json",
|
|
55
55
|
"description": "Parameter schema for the 'git_stash_list' MCP tool."
|
|
56
56
|
},
|
|
57
|
+
{
|
|
58
|
+
"name": "git_blame",
|
|
59
|
+
"file": "git_blame.json",
|
|
60
|
+
"description": "Parameter schema for the 'git_blame' MCP tool."
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"name": "git_branch_list",
|
|
64
|
+
"file": "git_branch_list.json",
|
|
65
|
+
"description": "Parameter schema for the 'git_branch_list' MCP tool."
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"name": "git_reflog",
|
|
69
|
+
"file": "git_reflog.json",
|
|
70
|
+
"description": "Parameter schema for the 'git_reflog' MCP tool."
|
|
71
|
+
},
|
|
57
72
|
{
|
|
58
73
|
"name": "git_fetch",
|
|
59
74
|
"file": "git_fetch.json",
|