@ahmedshaikh/git-workbench-mcp
v0.1.0
Published
Structured git for agents as an MCP server — the dirty tree as stable hunk IDs, non-interactive partial staging (commit exactly the hunks you name, hard-verified), and structured blame/history.
Maintainers
Readme
git-workbench-mcp
Structured git for agents — the version-control axis of the task lifecycle, both directions.
Everything before "tests pass" has structured tooling; after that, agents fall back to
shell gymnastics: parsing git status --porcelain, eyeballing 400-line diffs to mentally
group changes, and — the real blocker — no way to stage part of a file, because
git add -p is interactive and agent harnesses can't drive it. Going backwards in time
is the same: "why does this guard exist?" means re-deriving blame/log -S incantations
with unstructured output.
Tools
change_set — the dirty tree as stable hunk IDs
change_set
→ 2 changed file(s):
api.js [modified]
h_1be309f253 lines 1-5 (+1/-1): + return { id: String(id) }; // fix: ids are strings
h_8c5ef44ddc lines 10-14 (+1/-1): + console.log("access", user.id);
notes.txt [untracked] — whole-file only (select via paths=[...])Hunk IDs are content-addressed (path + hunk body, not line numbers), so they survive unrelated edits elsewhere in the file.
commit — non-interactive partial staging, hard-verified
commit message="fix: user ids are strings" hunk_ids=["h_1be309f253"]
→ committed 3359f51a: 1 file(s) — api.js
working tree still has uncommitted changes (as selected)One mixed working tree becomes several clean commits. A wrong partial stage would silently commit a state that never existed in the working tree — so the surgery is guarded on both sides:
- hunk IDs are re-derived from a fresh diff at commit time: if the tree moved since
change_set, the commit refuses (never fuzzy-applies) and tells you to re-select - after staging, the cached diff's hunk IDs must equal exactly the selection — any mismatch resets the index and commits nothing
- a dirty index is refused unless
reset_index=true(files are never touched)
Also does the boring cases: commit message=... commits everything;
paths=[...] commits whole files.
history — structured "why is this code like this?"
history file=app.py line_start=2 line_end=2 # blame mode
→ lines 2: 8a1f3c2d 2026-03-14 jane: add safety guard to top
history query="safety guard" # pickaxe mode
→ 1 commit(s) touching "safety guard":
8a1f3c2d 2026-03-14 jane: add safety guard to top
+ if True: # safety guard
history file=app.py # file log, renames followedInstall
// .mcp.json
{
"mcpServers": {
"git-workbench": {
"command": "npx",
"args": ["-y", "@ahmedshaikh/git-workbench-mcp"]
}
}
}Repo root defaults to the server cwd (GIT_WORKBENCH_ROOT or per-call cwd).
Limits
- renamed and binary files are whole-file only (no hunk addressing)
- quoted/unusual paths (spaces work; embedded quotes don't) — v1
- overlapping hunk selections that git cannot stage independently are refused with a suggestion to commit the whole file
- no push/PR tooling (deliberate: creating commits is the stall;
ghis not)
Development
npm install
npm test
npm run buildMIT
