Single source of truth for registered tool ids, client naming, JSON output shape, resource URI, and workspace root resolution.
Install and MCP clients (only canonical location): install.md. Preset file, dev, CI, publishing: HUMANS.md. Implementation layout (src/server/ + entry server.ts), contract bumps: AGENTS.md.
MCP clients expose tools as {serverName}_{toolName}. With the server registered as rethunk-git, examples use the prefix rethunk-git_.
| Short id | Client id (server rethunk-git) |
Purpose |
|---|---|---|
git_status |
rethunk-git_git_status |
git status --short -b per MCP root and optional submodules (includeSubmodules); parallel submodule status. Args include allWorkspaceRoots, rootIndex, workspaceRoot, format. |
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. |
git_parity |
rethunk-git_git_parity |
Compare git rev-parse HEAD for path pairs. pairs, preset, presetMerge, format, plus workspace pick args. |
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. |
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. |
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. |
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. Optional push: "after" pushes the current branch to its upstream once every commit lands. Args: commits (array of {message, files}), push?, plus workspace pick args + format. Mutating — not idempotent. |
git_merge |
rethunk-git_git_merge |
Merge one or more source branches into a destination. Default strategy auto cascades fast-forward → rebase → merge-commit per source, preferring linear history. Refuses on dirty tree; stops on first conflict with structured path report. Optional deleteMergedBranches / deleteMergedWorktrees cascade cleanup, always skipping protected names (main/master/dev/develop/stable/trunk/prod/production/release*/hotfix*). Args: sources, into?, strategy?, message?, cleanup flags + workspace pick + format. Mutating. |
git_cherry_pick |
rethunk-git_git_cherry_pick |
Play commits from one or more sources onto a destination. Sources may be SHAs, A..B ranges, or branch names (expanded to onto..<branch>, oldest-first). Uses --empty=drop so patch-equivalent re-applies add nothing. Refuses on dirty tree; stops on first conflict, aborting cleanly. Same cleanup flags as git_merge (branch-kind sources only, protected names skipped). Args: sources, onto?, cleanup flags + workspace pick + format. Mutating. |
Pass format: "json" on any tool for structured JSON instead of markdown (default).
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.
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.
git_inventory → inventories[*]
- Always present:
workspace_root,entries. - Omitted when not applicable:
presetSchemaVersion,nestedRootsTruncated,nestedRootsOmittedCount, and the wholeupstreamobject (emitted only when a fixedremote/branchpair was supplied; inautomode it is absent).
git_inventory → entries[*] (InventoryEntryJson)
- Always present:
label,path,upstreamMode("auto"or"fixed"). - Optional (omitted when empty/absent):
branchStatus,headAbbrev,upstreamRef,ahead,behind,upstreamNote,detached(only emitted astrue),skipReason(only on skipped entries). - Removed in v2:
shortStatus. The porcelain entries now live insidebranchStatus(the fullgit status --short -bbody — branch header line followed by porcelain lines).
Errors (any tool)
- Error payloads carry an
errorcode string and any structured context (e.g.preset,presetFile). The old free-textmessagefield is removed for self-describing codes (git_not_found,remote_branch_mismatch,invalid_remote_or_branch,no_pairs,preset_not_foundmissing case). It is retained only where it carries parse output (theinvalid_jsonpreset branch).
When to bump MCP_JSON_FORMAT_VERSION or change payload shape: AGENTS.md — Changing contracts.
| Parameter | Type | Default | Notes |
|---|---|---|---|
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). |
paths |
string[] | (all) | Restrict to commits touching these paths (appended after --). |
grep |
string | — | Filter by commit message regex (git --grep, always case-insensitive). |
author |
string | — | Filter by author name or email (--author=). |
maxCommits |
int | 50 |
Max commits per root. Hard cap: 500. |
branch |
string | HEAD |
Ref/branch to log from. |
workspaceRoot |
string | — | Explicit root; highest priority. |
rootIndex |
int | — | Pick one of several MCP roots (0-based). |
allWorkspaceRoots |
boolean | false |
Fan out across all MCP roots. |
format |
"markdown" | "json" |
"markdown" |
Output format. |
{
"groups": [{
"workspace_root": "/abs/path",
"repo": "my-repo",
"branch": "main",
"commits": [{
"sha7": "a1bf184",
"shaFull": "a1bf184c3d...",
"subject": "feat(satcom): upgrade to PROTOCOL_VERSION 4",
"author": "Damon Blais",
"email": "damon@example.com",
"date": "2026-04-12T18:32:01-07:00",
"ageRelative": "42m ago",
"filesChanged": 4,
"insertions": 16,
"deletions": 5
}],
"truncated": true,
"omittedCount": 12
}]
}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.
| Code | Meaning |
|---|---|
git_not_found |
git binary not on PATH. |
not_a_git_repo |
The resolved workspace root is not inside a git repository. |
invalid_since |
The since string contains shell metacharacters and was rejected. |
invalid_paths |
One of the paths entries contains shell metacharacters and was rejected. |
git_log_failed |
git log exited non-zero (e.g. unknown branch ref). |
root_index_out_of_range |
rootIndex exceeds the number of MCP file roots. |
| Parameter | Type | Default | Notes |
|---|---|---|---|
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. |
fileFilter |
string | — | Glob pattern to restrict output to matching files (e.g. "*.ts", "src/**"). |
maxLinesPerFile |
int | 50 |
Max diff lines to include per file (1–2000). |
maxFiles |
int | 30 |
Max files to include in output (1–500). |
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. |
workspaceRoot |
string | — | Explicit root; highest priority. |
rootIndex |
int | — | Pick one of several MCP roots (0-based). |
format |
"markdown" | "json" |
"markdown" |
Output format. |
{
"range": "unstaged changes",
"totalFiles": 2,
"totalAdditions": 10,
"totalDeletions": 5,
"files": [{
"path": "src/foo.ts",
"status": "modified",
"additions": 8,
"deletions": 3,
"truncated": false,
"diff": "@@ -1,3 +1,8 @@\n-const x = 1;\n+const x = 2;"
}],
"truncatedFiles": 1,
"excludedFiles": ["yarn.lock"]
}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).
| Code | Meaning |
|---|---|
git_not_found |
git binary not on PATH. |
not_a_git_repository |
The resolved workspace root is not inside a git repository. |
unsafe_range_token |
The range string contains characters outside the safe token set. |
git_diff_failed |
git diff exited non-zero. |
| Parameter | Type | Notes |
|---|---|---|
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. |
push |
"never" | "after" |
Default "never". "after" pushes the current branch to its upstream once all commits succeed. Never auto-sets upstream — branches without an upstream fail with push_no_upstream. Commits are not rolled back on push failure. Enum reserved for future modes such as "force-with-lease". |
workspaceRoot |
string | Explicit root; highest priority. |
rootIndex |
int | Pick one of several MCP roots (0-based). |
format |
"markdown" | "json" |
Output format. Default: "markdown". |
{
"ok": true,
"committed": 2,
"total": 2,
"results": [{
"index": 0,
"ok": true,
"sha": "a1b2c3d",
"message": "feat: add foo",
"files": ["src/foo.ts"]
}, {
"index": 1,
"ok": true,
"sha": "b2c3d4e",
"message": "chore: update config",
"files": ["config.json"]
}],
"push": {
"ok": true,
"branch": "main",
"upstream": "origin/main"
}
}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.
The push object is present only when push: "after" was requested and every commit landed. On push failure the top-level ok stays true (the commits themselves succeeded) while push.ok is false and push.error carries the code.
| Code | Meaning |
|---|---|
path_escapes_repository |
One of the listed file paths resolves outside the git toplevel. |
stage_failed |
git add failed (e.g. untracked path or permission error). |
commit_failed |
git commit failed (e.g. nothing staged, hooks rejected). |
not_a_git_repository |
The resolved workspace root is not inside a git repository. |
| Code | Meaning |
|---|---|
push_detached_head |
HEAD is detached; no branch to push. |
push_no_upstream |
Current branch has no configured upstream. batch_commit will not auto-set one — do git push -u origin <branch> yourself (or re-run without push). |
push_failed |
git push exited non-zero (network error, non-fast-forward, hook rejection). detail carries the stderr/stdout from git. |
| Parameter | Type | Notes |
|---|---|---|
sources |
string[] |
Source branches to merge, in order. 1–20 entries. Each must be a valid git ref token. |
into |
string | Destination branch. Defaults to the currently checked-out branch. Rejected when HEAD is detached. |
strategy |
"auto" | "ff-only" | "rebase" | "merge" |
Default "auto": cascade fast-forward → rebase → merge-commit per source. "ff-only" fails on divergence. "rebase" rebases source onto destination and fast-forwards; no merge-commit fallback. "merge" always creates a merge commit (--no-ff). |
message |
string | Merge commit message, used only when a merge commit is created. Defaults to Merge branch '<source>' into <into>. |
deleteMergedBranches |
boolean | Default false. After all sources land cleanly, delete each source branch locally (git branch -d). Protected names always skipped (main, master, dev, develop, stable, trunk, prod, production, release/*, release-*, hotfix/*, hotfix-*). Never touches remote refs. |
deleteMergedWorktrees |
boolean | Default false. After success, remove any local worktree currently checked out on a source branch (git worktree remove). Protected tails always skipped. |
workspaceRoot, rootIndex, format |
— | Standard workspace pick + output format. |
{
"ok": true,
"into": "main",
"strategy": "auto",
"headSha": "a1b2c3d4e5f6…",
"applied": 2,
"total": 2,
"results": [
{
"source": "feature/a",
"ok": true,
"outcome": "fast_forward",
"mergedSha": "a1b2c3d4e5f6…",
"branchDeleted": true,
"worktreeRemoved": "/tmp/agent-a"
},
{
"source": "feature/b",
"ok": true,
"outcome": "rebase_then_ff",
"mergedSha": "b2c3d4e5f6a1…"
}
]
}outcome (per source): fast_forward, rebase_then_ff, merge_commit, up_to_date, or conflicts. Cleanup fields (branchDeleted, worktreeRemoved) are only emitted when the corresponding flag was set and the operation actually ran — both are omitted for up-to-date sources and are never populated on partial-failure runs.
On conflict: top-level ok is false, the conflicting entry has ok: false with conflictStage ("rebase" or "merge"), conflictPaths (array of paths with unresolved markers), and an error code. Remaining sources are not attempted.
| Code | Meaning |
|---|---|
unsafe_ref_token |
A source or into contains characters outside the argv-safe subset (spaces, shell meta, .., @{, leading -, trailing .lock). |
into_detached_head |
HEAD is detached and no into was given — the tool needs a concrete destination branch. |
working_tree_dirty |
Uncommitted changes present. Commit, stash, or discard before merging. |
checkout_failed |
Could not switch to into. detail carries git's stderr. |
destination_not_found |
into does not resolve to a commit. |
not_a_git_repository |
The resolved workspace root is not inside a git repository. |
source_not_found (per source) |
A source branch name does not resolve. |
cannot_fast_forward (per source) |
strategy: "ff-only" refused because branches have diverged. |
rebase_conflicts (per source) |
Rebase encountered conflicts. Repo state is cleaned before returning. |
merge_conflicts (per source) |
Merge commit encountered conflicts. Repo state is cleaned before returning. |
merge_failed (per source) |
git merge --ff-only failed unexpectedly. detail carries stderr. |
merge_base_failed (per source) |
git merge-base failed (usually unrelated histories). |
| Parameter | Type | Notes |
|---|---|---|
sources |
string[] |
Source specs. 1–50 entries. Each entry is one of: a full/short SHA, an A..B / A...B range, or a branch name. Branch names expand to onto..<branch> (oldest-first). |
onto |
string | Destination branch. Defaults to the currently checked-out branch. Rejected when HEAD is detached. |
deleteMergedBranches |
boolean | Default false. After all commits apply, delete each branch-kind source locally (git branch -d) when it is fully merged into the destination by SHA-reachability (not patch-equivalence). Protected names always skipped; never touches remote refs. |
deleteMergedWorktrees |
boolean | Default false. After success, remove any local worktree attached to a branch-kind source (git worktree remove). Protected tails always skipped. |
workspaceRoot, rootIndex, format |
— | Standard workspace pick + output format. |
{
"ok": true,
"onto": "main",
"headSha": "a1b2c3d…",
"picked": 3,
"applied": 2,
"results": [
{ "source": "feature/a", "kind": "branch", "resolvedCommits": 2, "keptCommits": 2 },
{ "source": "abcdef1", "kind": "sha", "resolvedCommits": 1, "keptCommits": 1 }
]
}picked is the number of unique SHAs fed to git cherry-pick after SHA-reachability filtering. applied is the number of new commits actually added to HEAD — may be less than picked because the tool passes --empty=drop to git, so patch-equivalent commits are skipped at apply time without error.
kind is "sha", "range", or "branch". resolvedCommits is how many commits the source expanded to; keptCommits is how many survived SHA-reachability dedupe. Cleanup fields (branchDeleted, worktreeRemoved) are only emitted for branch-kind sources when the corresponding flag was set and the operation succeeded.
On conflict, the response has ok: false and a top-level conflict object:
{
"ok": false,
"onto": "main",
"picked": 2,
"applied": 0,
"results": [ ... ],
"conflict": {
"stage": "cherry-pick",
"commit": "abcdef1",
"paths": ["src/foo.ts"],
"detail": "…git stderr…"
}
}Repo state is cleaned (git cherry-pick --abort) before returning — no partially-applied index.
| Code | Meaning |
|---|---|
unsafe_ref_token |
A source or onto contains characters outside the argv-safe subset. |
onto_detached_head |
HEAD is detached and no onto was given. |
working_tree_dirty |
Uncommitted changes present. Commit, stash, or discard before cherry-picking. |
checkout_failed |
Could not switch to onto. |
destination_not_found |
onto does not resolve to a commit. |
source_not_found |
A source spec resolves to neither a branch, a range, nor a commit. |
range_resolution_failed |
git rev-list failed to expand a range spec. |
not_a_git_repository |
The resolved workspace root is not inside a git repository. |
| URI | Purpose |
|---|---|
rethunk-git://presets |
JSON snapshot of .rethunk/git-mcp-presets.json at the resolved git toplevel (or structured errors). |
Order applied when resolving which directory(ies) tools run against:
- Explicit
workspaceRooton the tool call (highest priority). rootIndex(0-based) — onefile://MCP root when several exist.allWorkspaceRoots: true — everyfile://root; markdown output emits one# {tool}header with per-root subsections (git_inventoryuses### {gitTop};git_statususes### MCP root: ...), or combined JSON.presetset and multiple roots — first root whose git toplevel defines that preset (respectingworkspaceRootHinton the preset entry when present).- Otherwise the first
file://root from MCPinitialize/roots/list_changed. process.cwd()if no file roots (e.g. CI with explicitworkspaceRoot).
Roots come from the MCP session (FastMCP with roots: { enabled: true } in code); there is no fixed cwd in server config.