user@gitdiot:~/blog/how-i-operate/cross-git-debugging$
● online ~/index ~/about
$ cat ./content/operating/cross-git-debugging.md --render
~/blog/how-i-operate/cross-git-debugging
how-i-operate git claude-code node mcp Mar 22, 2026 · 8 min read

Debugging across machines — two Claude instances collaborating through Git.

A remote MCP server was broken. Two Claude Code instances on different machines fixed it by communicating through Git commits. No SSH tunnel, no shared terminal — just commits as a communication protocol.

The Problem

A remote MCP (Model Context Protocol) server — claude-memory — was running on a remote Ubuntu server but couldn't connect to Claude Code on a local WSL machine. The server was healthy, auth worked, but every MCP request failed. A previous session had already diagnosed the likely issues and left a detailed handoff document in a Git repo.

The Setup

Two separate Claude Code instances, each on a different machine, with no direct communication channel between them. The only shared state: two GitHub repositories.

How It Worked

Step 1: Pick Up the Handoff

The previous session had written HANDOFF-REMOTE-MEMORY.md into the gitDiot-Org/Claude repo — a structured debugging guide with architecture details, known issues, curl test commands, and step-by-step instructions. The local Claude cloned the repo and read the document to get full context.

Step 2: Diagnose

Two issues were found:

Client-side: The claude mcp add CLI had split the Authorization header across two lines:

"Authorization": "Bearer\n  cm-de9d52ec..."

Server-side: The Express route used express.json() middleware before StreamableHTTPServerTransport.handleRequest. The middleware consumed the request stream to parse the body, leaving the transport with an empty stream — hence "Parse error: Invalid JSON" on every request.

Step 3: Fix and Communicate Through Commits

The local Claude fixed the client-side header directly, then fixed the server code and pushed with a commit message designed to be read by the remote Claude:

fix(transport): remove express.json() that breaks StreamableHTTPServerTransport

express.json() consumes the request stream before the transport can
read it, causing "Parse error: Invalid JSON" on every POST to /mcp.

Constraint: StreamableHTTPServerTransport reads raw body from request stream
Rejected: Re-buffering parsed body back into stream | unnecessary complexity
Confidence: high
Scope-risk: narrow

Then a second commit with explicit deployment instructions embedded in the message — where the remote Claude would see them on git pull.

Step 4: Poll and Wait

Three background polling loops were set up on the local machine:

Step 5: The Remote Claude Responds

Within minutes, the fragRag repo poller detected a new commit from the remote server:

aece4a2 server: Deployed transport fix, service restarted and healthy

The remote Claude had pulled the fix, rebuilt, restarted the service, and confirmed it was healthy — all communicated through a single commit message.

Step 6: Confirmation

The MCP endpoint poller confirmed the fix moments later:

[06:20:17] HTTP 400 — Parse error: Invalid JSON
[06:21:17] HTTP 400 — Parse error: Invalid JSON
...
[06:28:19] HTTP 200 — {"result":{"protocolVersion":"2025-03-26",...}}
SUCCESS — server is fixed!

Eight minutes of 400 errors, then a clean 200 with a valid MCP session.

Local Claude

  1. Read handoff doc
  2. Diagnosed both issues
  3. Pushed fix + deploy steps
  4. Polled for confirmation

Remote Claude

  1. Pulled the fix
  2. Ran npm run build
  3. Restarted the service
  4. Pushed deploy commit

What Made It Work

  1. Structured handoff documents — The original HANDOFF-REMOTE-MEMORY.md gave full context without requiring back-and-forth
  2. Commit messages as instructions — Deployment steps were embedded directly in the commit message where the remote Claude would see them on git pull
  3. Commit trailers for contextConstraint:, Rejected:, Confidence: trailers gave the remote Claude (and future humans) the reasoning behind choices
  4. Automated polling — Background loops eliminated the need to manually check for changes
  5. Verification built in — Both the curl test commands and the polling loops provided independent confirmation

Takeaway

Git is already the lingua franca of software collaboration. It turns out it works just as well as a communication protocol between AI agents on different machines. The commit history tells the full story: what broke, why, what was tried, what was rejected, and what ultimately fixed it — all in a format that both humans and AI can parse.


Related: Building a Persistent RAG Memory System for Claude Code — the architecture this debugging session was fixing.

subscribe.sh

Get the field notes

Weekly dispatches from an aging tech worker's refactoring. No spam, no thought leadership.

no spam · only high-signal logic