4 releases
Uses new Rust 2024
| new 0.2.1 | May 16, 2026 |
|---|---|
| 0.2.0 | May 16, 2026 |
| 0.1.2 | Apr 25, 2026 |
| 0.1.1 | Apr 10, 2026 |
#758 in Artificial intelligence
150KB
3K
SLoC
git-workflow-mcp
MCP server providing session-guarded git worktree management for AI agent pipelines.
Overview
git-workflow-mcp is a Model Context Protocol (MCP) server that exposes git operations as tools. It introduces session-based ownership — each MCP session gets a unique ID, and destructive operations (worktree removal, branch deletion, merge) are only allowed on resources created by the same session.
Tools
| Tool | Description | Session required |
|---|---|---|
session_start |
Initialize session with a git repository root | — |
worktree_add |
Create a worktree under .worktrees/ with a new branch |
Yes |
worktree_remove |
Remove a worktree (owner session only) | Yes |
worktree_list |
List all worktrees with ownership info | Yes |
branch_delete |
Delete a merged branch (owner session only) | Yes |
merge |
Merge a branch into target (owner session only) | Yes |
session_release |
Release session ownership of an orphan worktree | Yes |
commit |
Stage all changes and commit | No |
status |
Show git status | No |
diff |
Show git diff (stat + patch) | No |
log |
Show git log | No |
branch_status |
Return ahead/behind counts and commit lists as typed fields (no string parsing required) | No |
unpushed_commits |
List commits on a local branch not yet on its remote tracking ref | No |
is_pushed |
Check whether a commit is reachable from any remote tracking ref | No |
tag_pushed |
Check whether a tag exists on a remote by querying remote refs directly | No |
reset_target |
Compute the target commit hash N steps back via first-parent walk (no actual reset) | No |
worktree_state |
Return a typed snapshot of clean/ahead/behind/tracking/uncommitted in one call | No |
Modes
The server exposes different tool subsets depending on --mode:
--mode |
Exposed tools | Typical use |
|---|---|---|
full (default) |
All tools | Full worktree workflow (read + local write + remote) |
read-only |
status, diff, log, worktree_list, session_start |
Local read-only inspection |
read-remote |
read-only tools + fetch, remote_list |
Remote sync without local/remote write |
read-remote is a superset of read-only; full is a superset of read-remote.
No push, clone, or remote-configuration tools are exposed — those remain CLI-only.
Workflow
session_start → worktree_add → (work) → commit → merge → worktree_remove → branch_delete
Orphan Worktree Recovery
When an MCP session ends unexpectedly (e.g. a crash or timeout), the worktree it created remains registered under the original session ID. A new session cannot run merge, worktree_remove, or branch_delete on that worktree because session ownership does not match.
Use session_release to remove the ownership entry and unblock the new session:
# New session
session_start(repo_root)
session_release(name: "<worktree-name>") # drops orphan ownership entry
worktree_remove / merge / branch_delete # now succeeds
session_release is idempotent — calling it for an already-released or non-existent name always succeeds.
Structured Branch and Tag Inspection
Six read-only tools eliminate common AI git-parsing errors at BUMP/Publish moments.
ahead/behind without direction errors
branch_status returns ahead, behind, and up_to_date as discrete typed integer fields using git rev-list --left-right --count base...branch. There is no formatted string for the caller to parse, so direction-misread errors ("1 ahead" vs "1 behind") are structurally impossible.
branch_status(working_dir, branch: "main", base: "origin/main")
→ { ahead: 1, behind: 0, up_to_date: false, ahead_commits: [...], behind_commits: [], common_ancestor: "abc123" }
unpushed_commits lists the commits absent from the remote tracking ref as a typed list. is_pushed checks whether a specific commit hash is reachable from any remote ref.
All three tools assume remote refs are already fetched. Call fetch first when the remote state may have changed.
Tag remote visibility
tag_pushed queries the remote's tag refs directly via git ls-remote --tags and never inspects local tag metadata. This closes the gap where a tag created locally but not yet pushed appears "present" in local state.
tag_pushed(working_dir, tag: "v1.2.0", remote: "origin")
→ { pushed: true, remote_refs: ["refs/tags/v1.2.0"] }
Rollback target without merge-commit boundary crossing
reset_target computes the target commit hash by walking the first-parent chain for N steps (git log --first-parent), never using HEAD~N arithmetic. HEAD~N silently crosses merge commits and lands on a second-parent commit, making the rollback target unreliable.
reset_target(working_dir, steps_back: 2, from: "HEAD")
→ { target_hash: "def456", target_subject: "feat: ...", linear: true }
reset_target only computes the hash — it does not execute an actual reset.
Worktree state in one call
worktree_state combines branch_status, upstream tracking ref resolution, and uncommitted file count into a single typed response, giving a complete picture without multiple round-trips.
worktree_state(working_dir)
→ { clean: false, ahead: 1, behind: 0, tracking: "origin/main", uncommitted: 2 }
Installation
cargo install --path .
Configuration
Add to your MCP client configuration (e.g. Claude Code settings.json):
{
"mcpServers": {
"git-workflow": {
"command": "git-workflow-mcp",
"args": ["--stdio"]
}
}
}
Observability
The server writes structured logs to both stderr and a rolling file, useful for diagnosing disconnects or hangs after the fact.
- Logs: written to
$GIT_WORKFLOW_LOG_DIR(default~/.cache/git-workflow-mcp/) asmcp.YYYY-MM-DD.log(daily rotation). - Panic backtrace: on any thread panic, the backtrace is appended to
panic.login the same directory. - Log level:
--log-levelCLI arg >GIT_WORKFLOW_LOG_LEVELenv >RUST_LOGenv >warndefault. AcceptsEnvFiltersyntax (e.g.git_workflow_mcp=debug). - Heartbeat: an
alive pid=... sid=... elapsed_s=...info line every 30 seconds, so the last-alive timestamp is always within half a minute of a crash. - Shutdown reason: on exit a single
shutting down reason=<kind>line is emitted. Kinds:stdin_eof(transport closed normally),service_error(rmcp returned error),sigterm,sigpipe,ctrl_c.
License
Licensed under either of
at your option.
Dependencies
~15–22MB
~306K SLoC