@rethunk/mcp-multi-root-git 3.1.0 → 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.
- package/AGENTS.md +21 -15
- package/CHANGELOG.md +114 -39
- package/HUMANS.md +20 -39
- package/README.md +30 -13
- package/dist/repo-paths.js +57 -13
- package/dist/server/batch-commit-tool.js +187 -46
- package/dist/server/error-codes.js +25 -7
- package/dist/server/git-blame-tool.js +6 -3
- package/dist/server/git-branch-tool.js +151 -0
- package/dist/server/git-cherry-pick-tool.js +220 -11
- package/dist/server/git-conflicts-tool.js +230 -0
- package/dist/server/git-diff-summary-tool.js +36 -25
- package/dist/server/git-diff-tool.js +55 -27
- package/dist/server/git-grep-tool.js +188 -0
- package/dist/server/git-inventory-tool.js +26 -6
- package/dist/server/git-log-tool.js +35 -4
- package/dist/server/git-merge-tool.js +70 -12
- package/dist/server/git-parity-tool.js +13 -4
- package/dist/server/git-push-tool.js +3 -1
- package/dist/server/git-refs.js +48 -17
- package/dist/server/git-revert-tool.js +160 -0
- package/dist/server/git-show-tool.js +7 -3
- package/dist/server/git-stash-tool.js +110 -67
- package/dist/server/git-tag-tool.js +7 -6
- package/dist/server/git-worktree-tool.js +65 -53
- package/dist/server/git.js +116 -24
- package/dist/server/inventory.js +90 -32
- package/dist/server/presets-resource.js +37 -20
- package/dist/server/presets.js +87 -15
- package/dist/server/roots.js +13 -1
- package/dist/server/schemas.js +9 -2
- package/dist/server/test-harness.js +11 -4
- package/dist/server/tool-parameter-schemas.js +44 -55
- package/dist/server/tools.js +36 -17
- package/dist/server.js +1 -1
- package/docs/install.md +52 -5
- package/docs/mcp-tools.md +385 -178
- package/package.json +6 -6
- package/schemas/batch_commit.json +2 -2
- package/schemas/git_blame.json +1 -1
- package/schemas/git_branch.json +54 -0
- package/schemas/git_cherry_pick.json +12 -2
- package/schemas/git_cherry_pick_continue.json +34 -0
- package/schemas/{git_reflog.json → git_conflicts.json} +12 -12
- package/schemas/git_diff.json +11 -3
- package/schemas/git_grep.json +85 -0
- package/schemas/git_inventory.json +19 -5
- package/schemas/git_log.json +7 -6
- package/schemas/git_merge.json +2 -2
- package/schemas/git_parity.json +0 -5
- package/schemas/git_revert.json +47 -0
- package/schemas/git_show.json +1 -1
- package/schemas/git_stash_push.json +47 -0
- package/schemas/git_status.json +0 -5
- package/schemas/git_worktree_add.json +1 -1
- package/schemas/git_worktree_remove.json +1 -1
- package/schemas/index.json +28 -23
- package/schemas/list_presets.json +0 -5
- package/tool-parameters.schema.json +326 -167
- package/dist/server/git-branch-list-tool.js +0 -132
- package/dist/server/git-fetch-tool.js +0 -257
- package/dist/server/git-reflog-tool.js +0 -120
- package/schemas/git_branch_list.json +0 -30
- package/schemas/git_fetch.json +0 -46
- package/schemas/git_stash_list.json +0 -24
- package/schemas/git_worktree_list.json +0 -24
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
* const result = await tool({ workspaceRoot: dir, commits: [...] });
|
|
17
17
|
* // result is string (markdown) or JSON-parseable string
|
|
18
18
|
*/
|
|
19
|
+
import { afterEach } from "bun:test";
|
|
19
20
|
import { execFileSync } from "node:child_process";
|
|
20
21
|
import { appendFileSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
21
22
|
import { tmpdir } from "node:os";
|
|
@@ -33,12 +34,18 @@ const STUB_CONTEXT = {
|
|
|
33
34
|
};
|
|
34
35
|
// ---------------------------------------------------------------------------
|
|
35
36
|
// Tmp-dir lifecycle — prevents accumulating thousands of leaked test dirs.
|
|
36
|
-
// Each test file
|
|
37
|
-
//
|
|
38
|
-
//
|
|
39
|
-
//
|
|
37
|
+
// Each test file that calls mkTmpDir / makeRepo / trackTmpPath must register
|
|
38
|
+
// cleanup once at module scope:
|
|
39
|
+
// registerTmpCleanup();
|
|
40
|
+
// (equivalent to afterEach(cleanupTmpPaths) but documents the contract.)
|
|
41
|
+
// Module-scope afterEach(...) in this file would only register once
|
|
42
|
+
// (first-importer wins) because the module is cached across test files.
|
|
40
43
|
// ---------------------------------------------------------------------------
|
|
41
44
|
const tmpPaths = [];
|
|
45
|
+
/** Register afterEach cleanup for dirs created via mkTmpDir / makeRepo. Call once per test file. */
|
|
46
|
+
export function registerTmpCleanup() {
|
|
47
|
+
afterEach(cleanupTmpPaths);
|
|
48
|
+
}
|
|
42
49
|
export function mkTmpDir(prefix) {
|
|
43
50
|
const dir = mkdtempSync(join(tmpdir(), prefix));
|
|
44
51
|
tmpPaths.push(dir);
|
|
@@ -1,95 +1,84 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
import { registerGitFetchTool } from "./git-fetch-tool.js";
|
|
9
|
-
import { registerGitInventoryTool } from "./git-inventory-tool.js";
|
|
10
|
-
import { registerGitLogTool } from "./git-log-tool.js";
|
|
11
|
-
import { registerGitMergeTool } from "./git-merge-tool.js";
|
|
12
|
-
import { registerGitParityTool } from "./git-parity-tool.js";
|
|
13
|
-
import { registerGitPushTool } from "./git-push-tool.js";
|
|
14
|
-
import { registerGitReflogTool } from "./git-reflog-tool.js";
|
|
15
|
-
import { registerGitResetSoftTool } from "./git-reset-soft-tool.js";
|
|
16
|
-
import { registerGitShowTool } from "./git-show-tool.js";
|
|
17
|
-
import { registerGitStashApplyTool, registerGitStashListTool } from "./git-stash-tool.js";
|
|
18
|
-
import { registerGitStatusTool } from "./git-status-tool.js";
|
|
19
|
-
import { registerGitTagTool } from "./git-tag-tool.js";
|
|
20
|
-
import { registerGitWorktreeAddTool, registerGitWorktreeListTool, registerGitWorktreeRemoveTool, } from "./git-worktree-tool.js";
|
|
21
|
-
import { registerListPresetsTool } from "./list-presets-tool.js";
|
|
2
|
+
import { registerRethunkGitTools } from "./tools.js";
|
|
3
|
+
/**
|
|
4
|
+
* Fan-out read tools: polymorphic `root` routing (string | string[] | "*").
|
|
5
|
+
* Must stay in sync with the tools that use RootPickSchema — asserted against
|
|
6
|
+
* live `registerRethunkGitTools` capture in tests.
|
|
7
|
+
*/
|
|
22
8
|
export const FAN_OUT_ROOT_TOOLS = [
|
|
23
9
|
"git_status",
|
|
24
10
|
"git_inventory",
|
|
25
11
|
"git_parity",
|
|
26
12
|
"list_presets",
|
|
27
13
|
"git_log",
|
|
14
|
+
"git_grep",
|
|
28
15
|
];
|
|
16
|
+
/**
|
|
17
|
+
* Read-only single-repo tools: `workspaceRoot` routing only.
|
|
18
|
+
*/
|
|
29
19
|
export const READ_ONLY_SINGLE_REPO_TOOLS = [
|
|
30
20
|
"git_diff_summary",
|
|
31
21
|
"git_diff",
|
|
32
22
|
"git_show",
|
|
33
|
-
"
|
|
34
|
-
"git_stash_list",
|
|
23
|
+
"git_conflicts",
|
|
35
24
|
"git_blame",
|
|
36
|
-
"git_branch_list",
|
|
37
|
-
"git_reflog",
|
|
38
25
|
];
|
|
26
|
+
/**
|
|
27
|
+
* Mutating tools: `workspaceRoot` routing only.
|
|
28
|
+
*/
|
|
39
29
|
export const MUTATING_TOOLS = [
|
|
40
|
-
"git_fetch",
|
|
41
30
|
"batch_commit",
|
|
42
31
|
"git_push",
|
|
43
32
|
"git_merge",
|
|
44
33
|
"git_cherry_pick",
|
|
34
|
+
"git_cherry_pick_continue",
|
|
45
35
|
"git_reset_soft",
|
|
36
|
+
"git_revert",
|
|
46
37
|
"git_tag",
|
|
38
|
+
"git_branch",
|
|
47
39
|
"git_worktree_add",
|
|
48
40
|
"git_worktree_remove",
|
|
49
41
|
"git_stash_apply",
|
|
42
|
+
"git_stash_push",
|
|
50
43
|
];
|
|
44
|
+
/** Category union used for routing assertions; must equal live registrar names. */
|
|
51
45
|
export const ALL_PARAMETER_SCHEMA_TOOLS = [
|
|
52
46
|
...FAN_OUT_ROOT_TOOLS,
|
|
53
47
|
...READ_ONLY_SINGLE_REPO_TOOLS,
|
|
54
48
|
...MUTATING_TOOLS,
|
|
55
49
|
];
|
|
56
|
-
|
|
50
|
+
/**
|
|
51
|
+
* Capture parameter Zod schemas by driving the same registrar path as the live
|
|
52
|
+
* server (`registerRethunkGitTools`), so adding a tool only in `tools.ts` is
|
|
53
|
+
* enough for schema capture — no parallel register* list here.
|
|
54
|
+
*
|
|
55
|
+
* Clears `RETHUNK_GIT_TOOLS` for the duration so allowlist filtering cannot
|
|
56
|
+
* silently omit tools from published artifacts.
|
|
57
|
+
*/
|
|
58
|
+
export function captureToolParameterSchemas() {
|
|
57
59
|
const tools = [];
|
|
58
60
|
const server = {
|
|
59
61
|
sessions: [],
|
|
60
62
|
addTool(tool) {
|
|
61
63
|
tools.push({ name: tool.name, parameters: tool.parameters });
|
|
62
64
|
},
|
|
65
|
+
addResource() {
|
|
66
|
+
// Presets resource is always registered; capture only needs tools.
|
|
67
|
+
},
|
|
63
68
|
};
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
registerGitWorktreeListTool(server);
|
|
78
|
-
registerGitStashListTool(server);
|
|
79
|
-
registerGitFetchTool(server);
|
|
80
|
-
registerGitBlameTool(server);
|
|
81
|
-
registerGitBranchListTool(server);
|
|
82
|
-
registerGitReflogTool(server);
|
|
83
|
-
registerBatchCommitTool(server);
|
|
84
|
-
registerGitPushTool(server);
|
|
85
|
-
registerGitMergeTool(server);
|
|
86
|
-
registerGitCherryPickTool(server);
|
|
87
|
-
registerGitResetSoftTool(server);
|
|
88
|
-
registerGitTagTool(server);
|
|
89
|
-
registerGitWorktreeAddTool(server);
|
|
90
|
-
registerGitWorktreeRemoveTool(server);
|
|
91
|
-
registerGitStashApplyTool(server);
|
|
92
|
-
});
|
|
69
|
+
const prev = process.env.RETHUNK_GIT_TOOLS;
|
|
70
|
+
delete process.env.RETHUNK_GIT_TOOLS;
|
|
71
|
+
try {
|
|
72
|
+
registerRethunkGitTools(server);
|
|
73
|
+
}
|
|
74
|
+
finally {
|
|
75
|
+
if (prev === undefined) {
|
|
76
|
+
delete process.env.RETHUNK_GIT_TOOLS;
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
process.env.RETHUNK_GIT_TOOLS = prev;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
93
82
|
return Object.fromEntries(tools.map((tool) => [tool.name, z.toJSONSchema(tool.parameters)]));
|
|
94
83
|
}
|
|
95
84
|
export function buildToolParameterSchemaDocument() {
|
package/dist/server/tools.js
CHANGED
|
@@ -1,26 +1,27 @@
|
|
|
1
1
|
import { registerBatchCommitTool } from "./batch-commit-tool.js";
|
|
2
2
|
import { registerGitBlameTool } from "./git-blame-tool.js";
|
|
3
|
-
import {
|
|
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 {
|
|
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,
|
|
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,
|
|
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
|
|
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: "
|
|
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,
|
|
66
|
-
|
|
67
|
-
|
|
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
|
-
|
|
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 = "
|
|
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` |
|
|
95
|
-
| `
|
|
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:**
|
|
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
|
|
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
|
|