@rethunk/mcp-multi-root-git 2.9.1 → 4.0.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.
Files changed (75) hide show
  1. package/AGENTS.md +23 -19
  2. package/CHANGELOG.md +145 -39
  3. package/HUMANS.md +23 -42
  4. package/README.md +30 -13
  5. package/dist/repo-paths.js +57 -13
  6. package/dist/server/batch-commit-tool.js +203 -53
  7. package/dist/server/error-codes.js +31 -16
  8. package/dist/server/git-blame-tool.js +66 -19
  9. package/dist/server/git-branch-tool.js +151 -0
  10. package/dist/server/git-cherry-pick-tool.js +221 -12
  11. package/dist/server/git-conflicts-tool.js +230 -0
  12. package/dist/server/git-diff-summary-tool.js +39 -28
  13. package/dist/server/git-diff-tool.js +56 -31
  14. package/dist/server/git-grep-tool.js +188 -0
  15. package/dist/server/git-inventory-tool.js +30 -10
  16. package/dist/server/git-log-tool.js +37 -6
  17. package/dist/server/git-merge-tool.js +71 -13
  18. package/dist/server/git-parity-tool.js +15 -6
  19. package/dist/server/git-push-tool.js +4 -2
  20. package/dist/server/git-refs.js +48 -17
  21. package/dist/server/git-reset-soft-tool.js +1 -1
  22. package/dist/server/git-revert-tool.js +160 -0
  23. package/dist/server/git-show-tool.js +5 -10
  24. package/dist/server/git-stash-tool.js +112 -78
  25. package/dist/server/git-status-tool.js +2 -2
  26. package/dist/server/git-tag-tool.js +8 -13
  27. package/dist/server/git-worktree-tool.js +67 -59
  28. package/dist/server/git.js +116 -24
  29. package/dist/server/inventory.js +90 -32
  30. package/dist/server/list-presets-tool.js +2 -8
  31. package/dist/server/presets-resource.js +37 -20
  32. package/dist/server/presets.js +87 -15
  33. package/dist/server/roots.js +52 -79
  34. package/dist/server/schemas.js +18 -19
  35. package/dist/server/test-harness.js +11 -4
  36. package/dist/server/tool-parameter-schemas.js +47 -58
  37. package/dist/server/tools.js +36 -17
  38. package/dist/server.js +1 -1
  39. package/docs/install.md +52 -5
  40. package/docs/mcp-tools.md +472 -284
  41. package/package.json +6 -6
  42. package/schemas/batch_commit.json +5 -17
  43. package/schemas/git_blame.json +13 -11
  44. package/schemas/git_branch.json +54 -0
  45. package/schemas/git_cherry_pick.json +13 -15
  46. package/schemas/git_cherry_pick_continue.json +34 -0
  47. package/schemas/git_conflicts.json +38 -0
  48. package/schemas/git_diff.json +12 -10
  49. package/schemas/git_diff_summary.json +1 -15
  50. package/schemas/git_grep.json +85 -0
  51. package/schemas/git_inventory.json +32 -23
  52. package/schemas/git_log.json +20 -24
  53. package/schemas/git_merge.json +3 -15
  54. package/schemas/git_parity.json +13 -23
  55. package/schemas/git_push.json +1 -13
  56. package/schemas/git_reset_soft.json +1 -13
  57. package/schemas/git_revert.json +47 -0
  58. package/schemas/git_show.json +2 -8
  59. package/schemas/git_stash_apply.json +2 -8
  60. package/schemas/git_stash_push.json +47 -0
  61. package/schemas/git_status.json +13 -23
  62. package/schemas/git_tag.json +1 -7
  63. package/schemas/git_worktree_add.json +2 -14
  64. package/schemas/git_worktree_remove.json +2 -14
  65. package/schemas/index.json +28 -23
  66. package/schemas/list_presets.json +13 -23
  67. package/tool-parameters.schema.json +407 -423
  68. package/dist/server/git-branch-list-tool.js +0 -138
  69. package/dist/server/git-fetch-tool.js +0 -266
  70. package/dist/server/git-reflog-tool.js +0 -126
  71. package/schemas/git_branch_list.json +0 -36
  72. package/schemas/git_fetch.json +0 -52
  73. package/schemas/git_reflog.json +0 -44
  74. package/schemas/git_stash_list.json +0 -30
  75. package/schemas/git_worktree_list.json +0 -30
@@ -1,26 +1,27 @@
1
1
  import { registerBatchCommitTool } from "./batch-commit-tool.js";
2
2
  import { registerGitBlameTool } from "./git-blame-tool.js";
3
- import { registerGitBranchListTool } from "./git-branch-list-tool.js";
4
- import { registerGitCherryPickTool } from "./git-cherry-pick-tool.js";
3
+ import { registerGitBranchTool } from "./git-branch-tool.js";
4
+ import { registerGitCherryPickContinueTool, registerGitCherryPickTool, } from "./git-cherry-pick-tool.js";
5
+ import { registerGitConflictsTool } from "./git-conflicts-tool.js";
5
6
  import { registerGitDiffSummaryTool } from "./git-diff-summary-tool.js";
6
7
  import { registerGitDiffTool } from "./git-diff-tool.js";
7
- import { registerGitFetchTool } from "./git-fetch-tool.js";
8
+ import { registerGitGrepTool } from "./git-grep-tool.js";
8
9
  import { registerGitInventoryTool } from "./git-inventory-tool.js";
9
10
  import { registerGitLogTool } from "./git-log-tool.js";
10
11
  import { registerGitMergeTool } from "./git-merge-tool.js";
11
12
  import { registerGitParityTool } from "./git-parity-tool.js";
12
13
  import { registerGitPushTool } from "./git-push-tool.js";
13
- import { registerGitReflogTool } from "./git-reflog-tool.js";
14
14
  import { registerGitResetSoftTool } from "./git-reset-soft-tool.js";
15
+ import { registerGitRevertTool } from "./git-revert-tool.js";
15
16
  import { registerGitShowTool } from "./git-show-tool.js";
16
- import { registerGitStashApplyTool, registerGitStashListTool } from "./git-stash-tool.js";
17
+ import { registerGitStashApplyTool, registerGitStashPushTool } from "./git-stash-tool.js";
17
18
  import { registerGitStatusTool } from "./git-status-tool.js";
18
19
  import { registerGitTagTool } from "./git-tag-tool.js";
19
- import { registerGitWorktreeAddTool, registerGitWorktreeListTool, registerGitWorktreeRemoveTool, } from "./git-worktree-tool.js";
20
+ import { registerGitWorktreeAddTool, registerGitWorktreeRemoveTool } from "./git-worktree-tool.js";
20
21
  import { registerListPresetsTool } from "./list-presets-tool.js";
21
22
  import { registerPresetsResource } from "./presets-resource.js";
22
23
  /**
23
- * Ordered registry of all 23 MCP tools. Registration order is preserved for
24
+ * Ordered registry of all 24 MCP tools. Registration order is preserved for
24
25
  * both full and filtered (RETHUNK_GIT_TOOLS) subsets.
25
26
  */
26
27
  const TOOL_REGISTRARS = [
@@ -30,30 +31,36 @@ const TOOL_REGISTRARS = [
30
31
  { name: "git_parity", register: registerGitParityTool },
31
32
  { name: "list_presets", register: registerListPresetsTool },
32
33
  { name: "git_log", register: registerGitLogTool },
34
+ { name: "git_grep", register: registerGitGrepTool },
33
35
  { name: "git_diff_summary", register: registerGitDiffSummaryTool },
34
36
  { name: "git_diff", register: registerGitDiffTool },
35
37
  { name: "git_show", register: registerGitShowTool },
36
- { name: "git_worktree_list", register: registerGitWorktreeListTool },
37
- { name: "git_stash_list", register: registerGitStashListTool },
38
- { name: "git_fetch", register: registerGitFetchTool },
38
+ { name: "git_conflicts", register: registerGitConflictsTool },
39
39
  { name: "git_blame", register: registerGitBlameTool },
40
- { name: "git_branch_list", register: registerGitBranchListTool },
41
- { name: "git_reflog", register: registerGitReflogTool },
42
40
  // Mutating tools
43
41
  { name: "batch_commit", register: registerBatchCommitTool },
44
42
  { name: "git_push", register: registerGitPushTool },
45
43
  { name: "git_merge", register: registerGitMergeTool },
46
44
  { name: "git_cherry_pick", register: registerGitCherryPickTool },
45
+ { name: "git_cherry_pick_continue", register: registerGitCherryPickContinueTool },
47
46
  { name: "git_reset_soft", register: registerGitResetSoftTool },
47
+ { name: "git_revert", register: registerGitRevertTool },
48
48
  { name: "git_tag", register: registerGitTagTool },
49
+ { name: "git_branch", register: registerGitBranchTool },
49
50
  { name: "git_worktree_add", register: registerGitWorktreeAddTool },
50
51
  { name: "git_worktree_remove", register: registerGitWorktreeRemoveTool },
51
52
  { name: "git_stash_apply", register: registerGitStashApplyTool },
53
+ { name: "git_stash_push", register: registerGitStashPushTool },
52
54
  ];
53
55
  /**
54
56
  * Parse the RETHUNK_GIT_TOOLS env var and return the matching subset of
55
57
  * registrars plus any unrecognized token names.
56
58
  *
59
+ * Semantics:
60
+ * - unset / empty / whitespace-only → all tools
61
+ * - bare `*` (sole non-empty token) → all tools (all-tools sentinel; not an empty selection)
62
+ * - otherwise → exact name match (case-sensitive), canonical order, duplicates ignored
63
+ *
57
64
  * @param envValue Raw value of process.env.RETHUNK_GIT_TOOLS (may be undefined).
58
65
  * @param registrars Full ordered registrar list (injectable for tests).
59
66
  */
@@ -62,13 +69,25 @@ export function selectToolRegistrars(envValue, registrars) {
62
69
  .split(",")
63
70
  .map((t) => t.trim())
64
71
  .filter((t) => t.length > 0);
65
- // Unset, empty, or whitespace-only → register all tools.
66
- if (tokens.length === 0) {
67
- return { selected: registrars, unknown: [] };
72
+ // Unset, empty, whitespace-only, or bare "*" → register all tools.
73
+ // Bare "*" is an intentional all-tools sentinel (operators used to root="*"
74
+ // fan-out may set RETHUNK_GIT_TOOLS=* expecting the full surface — treating
75
+ // it as an unrecognized name that empties the allowlist is a footgun).
76
+ if (tokens.length === 0 || (tokens.length === 1 && tokens[0] === "*")) {
77
+ // Shallow copy so callers cannot mutate the shared TOOL_REGISTRARS array.
78
+ return { selected: [...registrars], unknown: [] };
68
79
  }
69
80
  const knownNames = new Set(registrars.map((r) => r.name));
70
81
  const requested = new Set(tokens);
71
- const unknown = tokens.filter((t) => !knownNames.has(t));
82
+ // Deduplicate unknown tokens while preserving first-seen order.
83
+ const unknownSeen = new Set();
84
+ const unknown = [];
85
+ for (const t of tokens) {
86
+ if (!knownNames.has(t) && !unknownSeen.has(t)) {
87
+ unknownSeen.add(t);
88
+ unknown.push(t);
89
+ }
90
+ }
72
91
  // Preserve canonical order; deduplicate duplicate tokens automatically.
73
92
  const selected = registrars.filter((r) => requested.has(r.name));
74
93
  return { selected, unknown };
@@ -81,7 +100,7 @@ export function registerRethunkGitTools(server) {
81
100
  }
82
101
  if (selected.length === 0 && (env ?? "").trim().length > 0) {
83
102
  process.stderr.write(`[rethunk-git] RETHUNK_GIT_TOOLS: every listed name was unrecognized — registering NO tools. ` +
84
- `Set RETHUNK_GIT_TOOLS to a comma-separated list of valid tool names, or unset it to register all tools.\n`);
103
+ `Set RETHUNK_GIT_TOOLS to a comma-separated list of valid tool names, bare "*" for all tools, or unset it to register all tools.\n`);
85
104
  }
86
105
  for (const { register } of selected) {
87
106
  register(server);
package/dist/server.js CHANGED
@@ -7,7 +7,7 @@ import { registerRethunkGitTools } from "./server/tools.js";
7
7
  * (renamed/nested/omitted fields). Surfaced via the FastMCP `instructions`
8
8
  * field below, so it is discoverable in the MCP `initialize` response.
9
9
  */
10
- export const MCP_JSON_FORMAT_VERSION = "3";
10
+ export const MCP_JSON_FORMAT_VERSION = "6";
11
11
  const server = new FastMCP({
12
12
  name: "rethunk-git",
13
13
  version: readMcpServerVersion(),
package/docs/install.md CHANGED
@@ -91,11 +91,30 @@ Omit any `cwd` / `workingDirectory` field unless your client requires it for unr
91
91
 
92
92
  | Variable | Default | Purpose |
93
93
  |----------|---------|---------|
94
- | `GIT_SUBPROCESS_PARALLELISM` | `4` | Number of parallel git subprocesses for inventory operations and `git_status` submodule rows. Valid range: 1 to 2×CPU count (auto-clamped to prevent runaway spawning). Increase on high-core machines to accelerate large fleet scans; decrease if system resources are constrained. |
95
- | `RETHUNK_GIT_TOOLS` | _(unset)_ | Comma-separated list of tool names to register. When unset or empty, all 23 tools are registered (default). When set, only the listed tools are exposed — unknown names are warned and ignored. If every name is unknown, **zero** tools are registered and a loud warning is emitted (the restriction is honored literally). The presets resource (`rethunk-git://presets`) is always registered regardless of this setting. Example: `RETHUNK_GIT_TOOLS=git_status,git_diff_summary,git_diff,git_log,batch_commit,git_push`. Full tool-name list: `git_status`, `git_inventory`, `git_parity`, `list_presets`, `git_log`, `git_diff_summary`, `git_diff`, `git_show`, `git_worktree_list`, `git_stash_list`, `git_fetch`, `git_blame`, `git_branch_list`, `git_reflog`, `batch_commit`, `git_push`, `git_merge`, `git_cherry_pick`, `git_reset_soft`, `git_tag`, `git_worktree_add`, `git_worktree_remove`, `git_stash_apply`. |
94
+ | `GIT_SUBPROCESS_PARALLELISM` | `4` | Max concurrent git subprocesses for `git_inventory` rows, `git_status` submodule rows, and multi-root fan-out in `git_log` and `git_grep`. Valid range: 1 to 2×CPU count (auto-clamped). Increase on high-core machines to accelerate large fleet scans; decrease if system resources are constrained. |
95
+ | `GIT_SUBPROCESS_TIMEOUT_MS` | `120000` | Per-subprocess timeout in milliseconds for async git calls. On expiry the child receives SIGTERM and the call fails. Set `0` (or negative) to disable timeout for intentionally unbounded operations. |
96
+ | `RETHUNK_GIT_TOOLS` | _(unset)_ | Comma-separated list of tool names to register. When unset or empty, all 24 tools are registered (default). When set, only the listed tools are exposed — unknown names are warned and ignored. If every name is unknown, **zero** tools are registered and a loud warning is emitted (the restriction is honored literally). The presets resource (`rethunk-git://presets`) is always registered regardless of this setting. Example: `RETHUNK_GIT_TOOLS=git_status,git_diff_summary,git_diff,git_log,batch_commit,git_push`. Full tool-name list: `git_status`, `git_inventory`, `git_parity`, `list_presets`, `git_log`, `git_grep`, `git_diff_summary`, `git_diff`, `git_show`, `git_conflicts`, `git_blame`, `batch_commit`, `git_push`, `git_merge`, `git_cherry_pick`, `git_cherry_pick_continue`, `git_reset_soft`, `git_revert`, `git_tag`, `git_branch`, `git_worktree_add`, `git_worktree_remove`, `git_stash_apply`, `git_stash_push`. |
96
97
 
97
98
  Set these in the environment where the MCP client launches the server (e.g. in your shell, in the MCP client config as `env`, or in a startup script).
98
99
 
100
+ **MCP client `env` block** (Cursor / Claude Desktop `mcpServers`; VS Code adds `"env"` beside `"command"` / `"args"`):
101
+
102
+ ```json
103
+ {
104
+ "mcpServers": {
105
+ "rethunk-git": {
106
+ "command": "npx",
107
+ "args": ["-y", "@rethunk/mcp-multi-root-git"],
108
+ "env": {
109
+ "GIT_SUBPROCESS_PARALLELISM": "8",
110
+ "GIT_SUBPROCESS_TIMEOUT_MS": "120000",
111
+ "RETHUNK_GIT_TOOLS": "git_status,git_log,batch_commit,git_push"
112
+ }
113
+ }
114
+ }
115
+ }
116
+ ```
117
+
99
118
  Example: Running the server with 8 parallel git processes on a 4-core machine:
100
119
 
101
120
  ```bash
@@ -200,12 +219,40 @@ Official protocol overview: [modelcontextprotocol.io](https://modelcontextprotoc
200
219
 
201
220
  For contributors working inside a clone of [mcp-multi-root-git](https://github.com/Rethunk-AI/mcp-multi-root-git):
202
221
 
203
- 1. **Dependencies, build, and CI parity:** **[HUMANS.md](../HUMANS.md)** — *Development* (`bun install`, `bun run build`, `bun run check`). Do not duplicate that workflow here.
222
+ 1. **Dependencies, build, and CI parity:** [CONTRIBUTING.md](../CONTRIBUTING.md) — *Development setup* (`bun install`, `bun run build`, `bun run ci`).
204
223
  2. **Run the dev server** (no `dist/` required): from the repo root, **`bun src/server.ts`** (stdio MCP).
205
224
 
206
- **MCP registration for a local checkout:** point your client at that command (or at **`dist/server.js`** via `node` after `bun run build` — see HUMANS). **Cursor:** add the command to either your user-scope or project-scope `mcp.json`, and open the workspace at the repository root so relative args resolve.
225
+ **MCP registration for a local checkout** set `cwd` to the repository root (exception to the no-`cwd` rule for published packages) so relative `args` resolve, or pass absolute paths in `args`. Open the workspace at the clone root in your client.
226
+
227
+ **Bun dev server** (no `dist/` build):
228
+
229
+ ```json
230
+ {
231
+ "mcpServers": {
232
+ "rethunk-git": {
233
+ "command": "bun",
234
+ "args": ["src/server.ts"],
235
+ "cwd": "/path/to/mcp-multi-root-git"
236
+ }
237
+ }
238
+ }
239
+ ```
240
+
241
+ **Built entrypoint** (after `bun run build`):
242
+
243
+ ```json
244
+ {
245
+ "mcpServers": {
246
+ "rethunk-git": {
247
+ "command": "node",
248
+ "args": ["dist/server.js"],
249
+ "cwd": "/path/to/mcp-multi-root-git"
250
+ }
251
+ }
252
+ }
253
+ ```
207
254
 
208
- **Reload** the MCP connection after changing server code.
255
+ **Cursor:** add either block to user-scope (`~/.cursor/mcp.json`) or project-scope (`.cursor/mcp.json`). **Reload** the MCP connection after changing server code.
209
256
 
210
257
  ## Troubleshooting
211
258