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.
- Local PC (WSL) — Claude Code diagnosing and fixing issues
- Remote server — Claude Code deploying changes and restarting services
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:
- MCP endpoint poll —
curlagainst the server every 60 seconds, waiting for HTTP 200 - fragRag repo poll —
git fetchevery 60 seconds, watching for new commits - Claude repo poll — same pattern on the config repo
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
- Read handoff doc
- Diagnosed both issues
- Pushed fix + deploy steps
- Polled for confirmation
Remote Claude
- Pulled the fix
- Ran
npm run build - Restarted the service
- Pushed deploy commit
What Made It Work
- Structured handoff documents — The original
HANDOFF-REMOTE-MEMORY.mdgave full context without requiring back-and-forth - Commit messages as instructions — Deployment steps were embedded directly in the commit message where the remote Claude would see them on
git pull - Commit trailers for context —
Constraint:,Rejected:,Confidence:trailers gave the remote Claude (and future humans) the reasoning behind choices - Automated polling — Background loops eliminated the need to manually check for changes
- 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.