@rethunk/mcp-multi-root-git 1.0.0 → 2.1.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 +37 -31
- package/HUMANS.md +8 -0
- package/README.md +3 -8
- package/dist/repo-paths.js +1 -1
- package/dist/server/batch-commit-tool.js +139 -0
- package/dist/server/git-diff-summary-tool.js +319 -0
- package/dist/server/git-inventory-tool.js +32 -84
- package/dist/server/git-log-tool.js +276 -0
- package/dist/server/git-parity-tool.js +2 -5
- package/dist/server/git-status-tool.js +11 -10
- package/dist/server/git.js +6 -27
- package/dist/server/inventory.js +59 -82
- package/dist/server/json.js +2 -9
- package/dist/server/list-presets-tool.js +1 -1
- package/dist/server/presets.js +10 -9
- package/dist/server/roots.js +12 -26
- package/dist/server/schemas.js +4 -16
- package/dist/server/test-harness.js +64 -0
- package/dist/server/tools.js +6 -0
- package/docs/mcp-tools.md +173 -12
- package/package.json +1 -1
package/docs/mcp-tools.md
CHANGED
|
@@ -15,27 +15,188 @@ MCP clients expose tools as `{serverName}_{toolName}`. With the server registere
|
|
|
15
15
|
| `git_inventory` | `rethunk-git_git_inventory` | Status + ahead/behind per path; default upstream each repo’s `@{u}`; pass **both** `remote` and `branch` for fixed tracking. `nestedRoots`, `preset`, `presetMerge`, `maxRoots`, `format`, plus workspace pick args. |
|
|
16
16
|
| `git_parity` | `rethunk-git_git_parity` | Compare `git rev-parse HEAD` for path pairs. `pairs`, `preset`, `presetMerge`, `format`, plus workspace pick args. |
|
|
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. |
|
|
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 + `format`. |
|
|
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 + `format`. **Read-only.** |
|
|
20
|
+
| `batch_commit` | `rethunk-git_batch_commit` | Create multiple sequential git commits in a single call. Each entry stages the listed files then commits with the given message. Stops on first failure. Args: `commits` (array of `{message, files}`), plus workspace pick args + `format`. **Mutating — not idempotent.** |
|
|
18
21
|
|
|
19
22
|
Pass **`format: "json"`** on any tool for structured JSON instead of markdown (default).
|
|
20
23
|
|
|
21
24
|
## JSON responses
|
|
22
25
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
26
|
+
Tool JSON bodies are minified and contain only the payload — no `rethunkGitMcp` envelope. Current `MCP_JSON_FORMAT_VERSION` is **`"2"`**; server + format version are discoverable via MCP `initialize`. Payload keys (`groups`, `inventories`, `parity`, `roots`) are stable within a given format version. Preset-related responses may include **`presetSchemaVersion`**.
|
|
27
|
+
|
|
28
|
+
### v2 field omission (consumer contract)
|
|
29
|
+
|
|
30
|
+
To keep responses compact, **optional fields are omitted when they would be empty, `null`, or `false`** — they are not emitted as `null`. Consumers must test for *presence*, not compare to `null`.
|
|
31
|
+
|
|
32
|
+
**`git_inventory` → `inventories[*]`**
|
|
33
|
+
|
|
34
|
+
- Always present: `workspace_root`, `entries`.
|
|
35
|
+
- Omitted when not applicable: `presetSchemaVersion`, `nestedRootsTruncated`, `nestedRootsOmittedCount`, and the whole `upstream` object (emitted only when a fixed `remote`/`branch` pair was supplied; in `auto` mode it is absent).
|
|
36
|
+
|
|
37
|
+
**`git_inventory` → `entries[*]` (`InventoryEntryJson`)**
|
|
38
|
+
|
|
39
|
+
- Always present: `label`, `path`, `upstreamMode` (`"auto"` or `"fixed"`).
|
|
40
|
+
- Optional (omitted when empty/absent): `branchStatus`, `headAbbrev`, `upstreamRef`, `ahead`, `behind`, `upstreamNote`, `detached` (only emitted as `true`), `skipReason` (only on skipped entries).
|
|
41
|
+
- **Removed in v2:** `shortStatus`. The porcelain entries now live inside `branchStatus` (the full `git status --short -b` body — branch header line followed by porcelain lines).
|
|
42
|
+
|
|
43
|
+
**Errors** (any tool)
|
|
44
|
+
|
|
45
|
+
- Error payloads carry an `error` code string and any structured context (e.g. `preset`, `presetFile`). The old free-text `message` field is **removed** for self-describing codes (`git_not_found`, `remote_branch_mismatch`, `invalid_remote_or_branch`, `no_pairs`, `preset_not_found` *missing* case). It is retained only where it carries parse output (the `invalid_json` preset branch).
|
|
46
|
+
|
|
47
|
+
**When to bump `MCP_JSON_FORMAT_VERSION` or change payload shape:** [AGENTS.md](../AGENTS.md) — *Changing contracts*.
|
|
48
|
+
|
|
49
|
+
### `git_log` — parameters
|
|
50
|
+
|
|
51
|
+
| Parameter | Type | Default | Notes |
|
|
52
|
+
|-----------|------|---------|-------|
|
|
53
|
+
| `since` | string | `"7.days"` | Passed to `git log --since=`. Accepts ISO timestamps (`2026-04-01T00:00:00Z`) or git relative forms (`48.hours`, `2.weeks.ago`). |
|
|
54
|
+
| `paths` | string[] | (all) | Restrict to commits touching these paths (appended after `--`). |
|
|
55
|
+
| `grep` | string | — | Filter by commit message regex (git `--grep`, always case-insensitive). |
|
|
56
|
+
| `author` | string | — | Filter by author name or email (`--author=`). |
|
|
57
|
+
| `maxCommits` | int | `50` | Max commits per root. Hard cap: `500`. |
|
|
58
|
+
| `branch` | string | `HEAD` | Ref/branch to log from. |
|
|
59
|
+
| `workspaceRoot` | string | — | Explicit root; highest priority. |
|
|
60
|
+
| `rootIndex` | int | — | Pick one of several MCP roots (0-based). |
|
|
61
|
+
| `allWorkspaceRoots` | boolean | `false` | Fan out across all MCP roots. |
|
|
62
|
+
| `format` | `"markdown"` \| `"json"` | `"markdown"` | Output format. |
|
|
63
|
+
|
|
64
|
+
### `git_log` — JSON shape (`format: "json"`)
|
|
65
|
+
|
|
66
|
+
```json
|
|
67
|
+
{
|
|
68
|
+
"groups": [{
|
|
69
|
+
"workspace_root": "/abs/path",
|
|
70
|
+
"repo": "my-repo",
|
|
71
|
+
"branch": "main",
|
|
72
|
+
"commits": [{
|
|
73
|
+
"sha7": "a1bf184",
|
|
74
|
+
"shaFull": "a1bf184c3d...",
|
|
75
|
+
"subject": "feat(satcom): upgrade to PROTOCOL_VERSION 4",
|
|
76
|
+
"author": "Damon Blais",
|
|
77
|
+
"email": "damon@example.com",
|
|
78
|
+
"date": "2026-04-12T18:32:01-07:00",
|
|
79
|
+
"ageRelative": "42m ago",
|
|
80
|
+
"filesChanged": 4,
|
|
81
|
+
"insertions": 16,
|
|
82
|
+
"deletions": 5
|
|
83
|
+
}],
|
|
84
|
+
"truncated": true,
|
|
85
|
+
"omittedCount": 12
|
|
86
|
+
}]
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
v2 field-omission rules: `filesChanged`, `insertions`, `deletions` are omitted when zero/absent (new file with no shortstat). `truncated` and `omittedCount` are omitted when `false`/`0`. A group emits `error` instead of `commits` when git fails for that root.
|
|
91
|
+
|
|
92
|
+
### `git_log` — error codes
|
|
93
|
+
|
|
94
|
+
| Code | Meaning |
|
|
95
|
+
|------|---------|
|
|
96
|
+
| `git_not_found` | `git` binary not on `PATH`. |
|
|
97
|
+
| `not_a_git_repo` | The resolved workspace root is not inside a git repository. |
|
|
98
|
+
| `invalid_since` | The `since` string contains shell metacharacters and was rejected. |
|
|
99
|
+
| `invalid_paths` | One of the `paths` entries contains shell metacharacters and was rejected. |
|
|
100
|
+
| `git_log_failed` | `git log` exited non-zero (e.g. unknown branch ref). |
|
|
101
|
+
| `root_index_out_of_range` | `rootIndex` exceeds the number of MCP file roots. |
|
|
102
|
+
|
|
103
|
+
### `git_diff_summary` — parameters
|
|
104
|
+
|
|
105
|
+
| Parameter | Type | Default | Notes |
|
|
106
|
+
|-----------|------|---------|-------|
|
|
107
|
+
| `range` | string | unstaged | Diff range. `"staged"` / `"cached"` for index; `"HEAD"` for last commit; `"A..B"` or `"A...B"` for revision ranges; single ref. Default: unstaged working-tree changes. |
|
|
108
|
+
| `fileFilter` | string | — | Glob pattern to restrict output to matching files (e.g. `"*.ts"`, `"src/**"`). |
|
|
109
|
+
| `maxLinesPerFile` | int | `50` | Max diff lines to include per file (1–2000). |
|
|
110
|
+
| `maxFiles` | int | `30` | Max files to include in output (1–500). |
|
|
111
|
+
| `excludePatterns` | string[] | lock files, dist, vendor | Glob patterns to exclude. Defaults to `*.lock`, `*.lockb`, `bun.lock`, `package-lock.json`, `yarn.lock`, `pnpm-lock.yaml`, `*.min.js`, `*.min.css`, `vendor/**`, `node_modules/**`, `dist/**`. Pass an empty array to disable. |
|
|
112
|
+
| `workspaceRoot` | string | — | Explicit root; highest priority. |
|
|
113
|
+
| `rootIndex` | int | — | Pick one of several MCP roots (0-based). |
|
|
114
|
+
| `format` | `"markdown"` \| `"json"` | `"markdown"` | Output format. |
|
|
115
|
+
|
|
116
|
+
### `git_diff_summary` — JSON shape (`format: "json"`)
|
|
117
|
+
|
|
118
|
+
```json
|
|
119
|
+
{
|
|
120
|
+
"range": "unstaged changes",
|
|
121
|
+
"totalFiles": 2,
|
|
122
|
+
"totalAdditions": 10,
|
|
123
|
+
"totalDeletions": 5,
|
|
124
|
+
"files": [{
|
|
125
|
+
"path": "src/foo.ts",
|
|
126
|
+
"status": "modified",
|
|
127
|
+
"additions": 8,
|
|
128
|
+
"deletions": 3,
|
|
129
|
+
"truncated": false,
|
|
130
|
+
"diff": "@@ -1,3 +1,8 @@\n-const x = 1;\n+const x = 2;"
|
|
131
|
+
}],
|
|
132
|
+
"truncatedFiles": 1,
|
|
133
|
+
"excludedFiles": ["yarn.lock"]
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
`status` is one of `"modified"`, `"added"`, `"deleted"`, `"renamed"`. `oldPath` is present only for renamed files. `truncatedFiles` and `excludedFiles` are omitted when zero/empty (v2 field-omission contract).
|
|
138
|
+
|
|
139
|
+
### `git_diff_summary` — error codes
|
|
140
|
+
|
|
141
|
+
| Code | Meaning |
|
|
142
|
+
|------|---------|
|
|
143
|
+
| `git_not_found` | `git` binary not on `PATH`. |
|
|
144
|
+
| `not_a_git_repository` | The resolved workspace root is not inside a git repository. |
|
|
145
|
+
| `unsafe_range_token` | The `range` string contains characters outside the safe token set. |
|
|
146
|
+
| `git_diff_failed` | `git diff` exited non-zero. |
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
### `batch_commit` — parameters
|
|
151
|
+
|
|
152
|
+
| Parameter | Type | Notes |
|
|
153
|
+
|-----------|------|-------|
|
|
154
|
+
| `commits` | `{message: string, files: string[]}[]` | Commits to create in order. 1–50 entries. Each `files` entry is a path relative to the git root; all must stay within the git toplevel. |
|
|
155
|
+
| `workspaceRoot` | string | Explicit root; highest priority. |
|
|
156
|
+
| `rootIndex` | int | Pick one of several MCP roots (0-based). |
|
|
157
|
+
| `format` | `"markdown"` \| `"json"` | Output format. Default: `"markdown"`. |
|
|
158
|
+
|
|
159
|
+
### `batch_commit` — JSON shape (`format: "json"`)
|
|
160
|
+
|
|
161
|
+
```json
|
|
162
|
+
{
|
|
163
|
+
"ok": true,
|
|
164
|
+
"committed": 2,
|
|
165
|
+
"total": 2,
|
|
166
|
+
"results": [{
|
|
167
|
+
"index": 0,
|
|
168
|
+
"ok": true,
|
|
169
|
+
"sha": "a1b2c3d",
|
|
170
|
+
"message": "feat: add foo",
|
|
171
|
+
"files": ["src/foo.ts"]
|
|
172
|
+
}, {
|
|
173
|
+
"index": 1,
|
|
174
|
+
"ok": true,
|
|
175
|
+
"sha": "b2c3d4e",
|
|
176
|
+
"message": "chore: update config",
|
|
177
|
+
"files": ["config.json"]
|
|
178
|
+
}]
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
On first failure `ok` is `false`, `committed` reflects only the entries that succeeded before the error, and the failing entry includes `error` and `detail` fields. Remaining entries are skipped and not included in `results`.
|
|
183
|
+
|
|
184
|
+
### `batch_commit` — error codes (per-result `error` field)
|
|
185
|
+
|
|
186
|
+
| Code | Meaning |
|
|
187
|
+
|------|---------|
|
|
188
|
+
| `path_escapes_repository` | One of the listed file paths resolves outside the git toplevel. |
|
|
189
|
+
| `stage_failed` | `git add` failed (e.g. untracked path or permission error). |
|
|
190
|
+
| `commit_failed` | `git commit` failed (e.g. nothing staged, hooks rejected). |
|
|
191
|
+
| `not_a_git_repository` | The resolved workspace root is not inside a git repository. |
|
|
192
|
+
|
|
193
|
+
---
|
|
33
194
|
|
|
34
195
|
## Resource
|
|
35
196
|
|
|
36
197
|
| URI | Purpose |
|
|
37
198
|
|-----|---------|
|
|
38
|
-
| `rethunk-git://presets` | JSON snapshot of `.rethunk/git-mcp-presets.json` at the resolved git toplevel (or structured errors
|
|
199
|
+
| `rethunk-git://presets` | JSON snapshot of `.rethunk/git-mcp-presets.json` at the resolved git toplevel (or structured errors). |
|
|
39
200
|
|
|
40
201
|
## Workspace root resolution
|
|
41
202
|
|
|
@@ -43,7 +204,7 @@ Order applied when resolving which directory(ies) tools run against:
|
|
|
43
204
|
|
|
44
205
|
1. Explicit **`workspaceRoot`** on the tool call (highest priority).
|
|
45
206
|
2. **`rootIndex`** (0-based) — one `file://` MCP root when several exist.
|
|
46
|
-
3. **`allWorkspaceRoots`: true** — every `file://` root; markdown
|
|
207
|
+
3. **`allWorkspaceRoots`: true** — every `file://` root; markdown output emits one `# {tool}` header with per-root subsections (`git_inventory` uses `### {gitTop}`; `git_status` uses `### MCP root: ...`), or combined JSON.
|
|
47
208
|
4. **`preset`** set and multiple roots — first root whose git toplevel defines that preset (respecting **`workspaceRootHint`** on the preset entry when present).
|
|
48
209
|
5. Otherwise the first `file://` root from MCP **`initialize`** / **`roots/list_changed`**.
|
|
49
210
|
6. **`process.cwd()`** if no file roots (e.g. CI with explicit `workspaceRoot`).
|
package/package.json
CHANGED