AI agents · 2026-05-28

AI agents and Git, without handing over the keys.

AI coding agents are useful when they understand a repository. They are dangerous when they can mutate it silently. Most Git MCP servers pick a side: read-only, so the agent stays harmless but limited, or direct write tools, so a single hallucination can rewrite history. FluxGit picks a third path. Agents propose. The desktop app shows the preview. The user approves. The app executes through the same safety pipeline a manual click would have used.

The two failure modes today.

A strictly read-only Git MCP is the safe default and the path most servers take. The agent can explain why a branch is divergent, summarize a diff, suggest a rebase strategy, walk the reflog after a bad reset. It cannot help do any of it. The user reads the explanation and then context-switches to a terminal or a GUI to actually move the refs. The agent is a research assistant trapped behind glass.

A direct-write Git MCP is the other extreme. The agent gets merge, rebase, reset, apply-patch as ordinary tools and decides for itself when to call them. This works beautifully in demos and breaks badly in production. Hallucinations, prompt injection from a malicious README, or a model that has misunderstood the task all become destructive Git operations. The audit trail says the agent did it, but nobody approved it.

The third path needs something neither extreme has: an application that can render an approval modal. An MCP server alone cannot ask the user a question; it answers tool calls. Wire an MCP server to a desktop app you already trust for Git, and the loop closes. Agents propose, the app shows the preview, you decide.

What shipped on 2026-05-28.

The full surface is now live in FluxGit's MCP layer. Eighteen read-only tools cover repository inspection: repo.status, repo.refs, repo.branchStack, repo.history, repo.reflog, commit.details, worktree.changes, submodule.status, diff.text, diff.semantic with explicit fallback, diff.semanticFallbacks, repo.conflictPreflight, fleet.radar, safety.timeline, safety.eventDetails, flux.latestRestorePoint, flux.restorePoints, flux.restorePointDetails. Each call returns a structured payload tagged with source: "local-git" or source: "fluxgit-app", so the agent always knows whether the data came from raw git or from FluxGit's enriched layer.

Five write tools complete the loop, and they all dispatch through the same UI handshake: operation.preview.merge, operation.preview.rebase, operation.preview.discard, operation.preview.reset, operation.preview.patch. None of them touch refs directly. Each one opens an approval card in the desktop app, pre-filled with the agent's reason in plain language, and waits for the user.

The handshake itself is a simple HTTP loop. The sidecar POSTs the proposal to a loopback gateway. The gateway mounts an approval card inside the FluxGit app via the existing Tauri bridge. The user clicks Approve or Reject. On approval, FluxGit calls the same internal function a manual operation would have called - the merge runs through mergeRefIntoCurrentBranch, the rebase runs through the existing rebase action, and so on - so restore points, preflight checks and the safety timeline all apply unchanged. On rejection, the operation stops and the audit log records the reason. The sidecar polls for the result and returns it to the agent.

The wire contract is forward-stable. Clients written today against operation.preview.merge keep working when the other four operations land in their MCP host's UI. The protocol carries an explicit operationType field so audit consumers can route by type without parsing URLs, and a previewId that threads through every layer: agent intent, user decision, executed commit. If the handshake server is unreachable the sidecar returns error code 10003 (write_handshake_pending) instead of silently failing, which keeps the contract honest for hosts that connect before FluxGit is running.

The audit log is opt-in Ed25519 signed. Set FLUXGIT_MCP_AUDIT_SIGN_KEY to a PEM PKCS8 private key and every appended entry carries a signature over the canonical JSON of the entry (the signature field stripped, keys sorted lexicographically, default compact serialization). A public verify-audit CLI subcommand walks the JSONL with the matching public key and prints verified / failed / unsigned / malformed counts. Unsigned entries are counted separately so a team can roll signing out incrementally without breaking the verifier on older history. This is the bridge between AI provenance and human accountability: the audit chain links the agent's intent, the user's approval, and the resulting commit SHA, and that link can be cryptographically verified after the fact.

What the agent sends.

Every write proposal is a small JSON body with the operation type, the refs or paths involved, a free-form reason, and a previewId. Here is what an MCP-compatible code agent sends when it wants to merge a feature branch into main:

POST /v1/mcp/operation/preview/merge
{
  "previewId": "a8f3-7c21-...",
  "agentId": "external-mcp-sidecar",
  "operationType": "merge",
  "repoPath": "/Users/eng/work/checkout",
  "sourceRef": "feature/checkout-redesign",
  "targetRef": "main",
  "reason": "Checkout redesign work is complete; tests pass on the feature branch.",
  "strategy": "merge",
  "requestedAt": "2026-05-28T09:50:11.123Z"
}

The gateway responds 202 Accepted immediately and the agent polls the shared status endpoint until the user makes a decision. The completion payload carries the commit SHA and the restore point ID - facts the agent could not have invented:

GET /v1/mcp/operation/status/a8f3-7c21-...
{
  "previewId": "a8f3-7c21-...",
  "operationType": "merge",
  "status": "completed",
  "result": {
    "commitSha": "9b7c4e2f...",
    "restorePointId": "rp_2026_05_28_0950_11",
    "conflicts": null
  }
}

If the user rejects, status becomes rejected and the payload carries an optional rejectionReason. If the proposal expires (default TTL is five minutes), status becomes expired. The agent treats anything other than completed as a non-error signal to ask the user what happened in plain language, not as a transport failure.

One concrete narrative.

An engineer asks their code agent to merge feature/checkout-redesign into main. The agent calls repo.status to confirm the working tree is clean, asks diff.semantic for a structured view of the change (and gracefully falls back to diff.text if the semantic engine is not wired), then calls operation.preview.merge with a one-sentence reason. FluxGit mounts an approval card inside the desktop app with a banner reading "Requested by AI agent", the source and target refs, the agent's reason in plain language, and a note that a restore point will be created before the merge applies.

The engineer reads the card. The diff matches what they expected. They click Approve. The modal swaps to an "Applying merge..." spinner while FluxGit captures the restore point and runs the merge through the same internal function a manual merge would have used. The history view updates. The safety timeline gains an entry. The audit log records the agent's intent, the engineer's approval, the preview ID, and the resulting commit SHA - signed end to end if signing is on. The agent's last message updates with the commit SHA and the restore point ID, and the loop closes. Total elapsed time: about seventy-five seconds.

What is not shipped - explicit honesty.

A few pieces are still in flight, and saying so is part of the contract. Interactive rebase is rejected at the approval pipeline because the handshake does not carry an interactive step list yet; non-interactive rebase works end to end. Real-time agent-to-app notification is implemented as a 1.5-second poll, not a push channel, which is fine for one developer but will need a push transport for shared deployments. A cloud-hosted MCP for teams who do not want to install the desktop app on every machine is on the roadmap; the current sidecar is stdio only. The launch demo video is in the can but not yet edited and posted. Everything in the section above is shipped code with tests; everything in this paragraph is honest about what comes next.

Why this matters.

The Model Context Protocol is open. FluxGit speaks it like everyone else, and the standalone shell works without the FluxGit app for the eight tools that need only local git. That buys distribution: any MCP-compatible code agent can connect, no vendor lock-in, no special client wiring. Per playbook stance, the generic JSON config block is the default; per-host cheat sheets, when added, will live behind a neutral dropdown rather than as the primary CTA.

The interesting parts of the surface require the desktop app, and that is by design rather than by accident. The safety timeline is synthesized from FluxGit restore points and the reflog. The approval modal lives inside the app's existing UI shell. The write handshake terminates at a Tauri command bridge that calls the same internal Git actions a manual click would call. Cloud-hosted MCP servers without an app cannot do any of this - they have no UI to render an approval card in. That is the business model and the moat at the same time.

The practical pitch for developers is short. Let the agent do the planning. Keep yourself in the loop on the writes. Pay nothing for inspection - the free shell speaks MCP and answers repo.status, diff.text, the rest of the read-only surface against local git. Pay FluxGit when you want the safety nets: restore points, predictive conflict preflight, the safety timeline, fleet radar, the signed audit chain, and the approval modal that turns operation.preview.merge from a slogan into a closed loop.