AI agents · 2026-05-28 · updated 2026-06-11

AI agents and Git, without handing over the keys.

The AI-coding wave has a quiet failure mode nobody likes to talk about: when you let an agent run git for you, it can commit things you never reviewed. People have already watched agents auto-commit secrets straight into git history, which is immutable, so the secret is there forever even after you "delete" it. FluxGit makes that preventable inside the integration by construction rather than policy: FluxGit MCP exposes no direct Git mutation tool. Twelve operation tools create proposals and one cancels an agent's own pending proposal. The desktop app shows an operation-specific preview, risk and available recovery context; only an approved proposal runs through FluxGit. A separate shell granted by the agent host remains outside this boundary.

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.

This is also why the secrets scenario from the opening cannot happen here. A leaked credential reaches history through a commit nobody read. In FluxGit's model there is no commit the human did not read: the proposed change is rendered as a real diff in the approval card, so an .env file or an API key sitting in the patch is staring at you before anything touches the repository. And the whole handshake runs on loopback between the sidecar, the gateway and the app on your machine; the proposal never transits a cloud service.

What shipped on 2026-05-28, and what landed since.

The full surface is live in FluxGit's MCP layer. Twenty-five read-only tools cover repository inspection: repo.brief (one-call situational awareness, advertised first because it replaces the 6-10 raw git calls an agent burns just learning where it is), repo.scope (everything about one monorepo subtree in a single call), repo.status, repo.refs, repo.branchStack, repo.history, repo.reflog, commit.details, worktree.changes, worktree.list, submodule.status, diff.text, diff.semantic (real structural payload when the app is running, explicit fallback standalone), diff.semanticFallbacks, repo.conflictPreflight, conflict.read (an active conflict as structured data instead of raw markers, shipped 2026-06-11), fleet.radar, fleet.digest (reflog activity across repositories, shipped 2026-09-28), agents.presence (other coding agents in the same repository, shipped 2026-09-28), safety.timeline, safety.eventDetails, flux.latestRestorePoint, flux.restorePoints, flux.restorePointDetails, and operation.status, the read-only poll of a proposal's lifecycle. 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.

Twelve proposal tools complete the write loop, and they all dispatch through the same UI handshake. They cover the agent's full working cycle: operation.preview.branch, operation.preview.commit and operation.preview.push alongside operation.preview.merge, operation.preview.rebase, operation.preview.discard, operation.preview.reset, operation.preview.patch, operation.preview.worktree, operation.preview.branchDelete, operation.preview.submodulePointer and operation.preview.plan, which lets an agent propose up to ten steps as one reviewable plan with a single approval and stop-on-failure execution. Separately, operation.cancel lets an agent withdraw its own pending proposal; it mutates handshake state, never the repository. None of the sidecar tools touch refs directly. Each proposal 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 is an asynchronous HTTP loop. The sidecar POSTs the proposal to a loopback gateway, performs one bounded status read, then normally returns accepted: true, the canonical previewId, the current status and nextAction.tool: "operation.status". The gateway mounts an approval card inside FluxGit via the existing Tauri bridge. The user clicks Approve or Reject, and the client follows the lifecycle through the read-only operation.status tool. On approval, FluxGit calls the same guarded internal operation a manual action would use. The audit log records the lifecycle and real outcome.

The wire contract is forward-stable. Clients written against the original operation.preview.merge kept working unchanged when branch, commit, push and worktree proposals landed. 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 and every entry carries a signature, with a verify-audit CLI that checks the whole log and reports pass/fail counts. Unsigned entries are counted separately, so a team can roll signing out incrementally. This is the bridge between AI provenance and human accountability: the audit chain links the agent's intent, proposal lifecycle and terminal outcome, including approval, rejection, cancellation, failure or expiry.

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": "/workspace/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. After one bounded status read, the preview tool normally returns an accepted proposal with a nextAction pointing to operation.status. The client uses that read-only tool until the proposal reaches a terminal state. A completed merge can return its commit SHA and any restore-point metadata actually captured by that operation:

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
  }
}

Live pending and approved states are successful accepted results without claiming Git changed. Only completed reports a successful Git outcome. Rejected, failed, expired and cancelled terminal states return isError: true with a structured payload the agent can explain accurately.

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 (with the app running it gets real per-file structural hunks; standalone the tool routes it honestly to diff.text), 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, risk and the available recovery context.

The engineer reads the card. The operation preview 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 when one was captured, and the loop closes.

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 most of the read-only surface: thirteen tools served purely from local git, plus four hybrids (fleet radar, semantic diff and conflict preflight among them) that answer locally and say so honestly when the app is not wired. Five safety and restore-point tools require FluxGit, and operation.status tracks proposals accepted by the app. 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 the free shell (fluxgit-hq/fluxgit-mcp-server, Apache-2.0): 13 tools inspect local Git directly and four hybrid tools provide documented local fallbacks. Five safety tools require the FluxGit app, while read-only operation.status follows an accepted proposal. 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.