Building an MCP Server for Git-Based AI Memory: Lessons from ShadowGit
The Problem
Every time you start a new Claude conversation, you paste your entire codebase. Again. The AI has no memory of what you worked on yesterday, what solutions failed, or what actually fixed that bug last week.
I built ShadowGit to solve this: automatic code snapshots with an MCP server that gives AI assistants searchable history. Here's what I learned.
The Technical Approach
Auto-Commit System
The polling orchestrator checks for changes every 5 seconds (configurable):
// From polling-orchestrator.ts
private pollingInterval: number = 5000; // Default 5 seconds
const scheduleNextPoll = async () => {
if (!this.isPollingInProgress) {
await this.pollAllRepositories();
}
this.pollingTimer = setTimeout(scheduleNextPoll, this.pollingInterval);
};
This creates a .shadowgit.git
repository parallel to your main repo - a proper git repository, not just a folder:
// Commit messages are intelligent, not just timestamps
generateMessage(repoPath, shadowgitDir, changedFiles, totalLines, isAISession) {
const timestamp = new Date().toISOString().replace(/T/, ' ').replace(/\..+/, '');
if (isAISession) {
return `๐ค AI-Assisted Changes @ ${timestamp}
๐ง AI Tool Session Detected
๐ Types: ${fileTypes.join(', ')}
๐ Impact: ${totalLines} lines changed`;
}
// Regular commits have different format
}
MCP Server Implementation
The MCP server exposes git operations through a clean protocol:
{
name: 'git_command',
description: 'Execute a read-only git command on a ShadowGit repository',
inputSchema: {
type: 'object',
properties: {
repo: { type: 'string', description: 'Repository name' },
command: { type: 'string', description: 'Git command to execute' }
},
required: ['repo', 'command']
}
}
Now Claude can run:
-
git_command({repo: "my-app", command: "diff HEAD~5"})
- See recent changes -
git_command({repo: "my-app", command: "log --grep='auth'"})
- Search commits -
git_command({repo: "my-app", command: "show HEAD~10:src/file.ts"})
- Retrieve old versions
The GitExecutor validates every command for safety:
// Security: Only allow safe read-only commands
private readonly ALLOWED_COMMANDS = [
'log', 'diff', 'show', 'status', 'ls-files', 'blame', 'shortlog'
];
private readonly BLOCKED_ARGS = [
'--git-dir', '--work-tree', '-C', // Prevent directory traversal
'push', 'pull', 'fetch', 'merge', 'rebase' // No modifications
];
Session Management
When AI actively works on code, you don't want fragmented commits every 5 seconds. The session system is elegant:
// MCP tools for session management
{
name: 'start_session',
description: 'Start a work session. MUST be called BEFORE making changes',
inputSchema: {
properties: {
repo: { type: 'string' },
description: { type: 'string' }
}
}
}
// Session handler communicates with the ShadowGit app
async startSession(args) {
const sessionId = await this.sessionClient.startSession({
repoPath,
aiTool: 'MCP Client',
description: args.description
});
// ShadowGit pauses auto-commits for this repo
// Returns session ID for later use
return `Session started: ${sessionId}`;
}
The SessionManager tracks active sessions with SQLite:
// Session storage with automatic expiration
class SessionManager {
private readonly SESSION_EXPIRY_MS = 60 * 60 * 1000; // 1 hour
async createSession(repoPath, tool, description) {
const session = {
id: uuidv4(),
repoPath,
tool,
startTime: Date.now(),
expiresAt: Date.now() + this.SESSION_EXPIRY_MS
};
// Store in SQLite for persistence
}
}
Architecture Deep Dive
The Two-Component Design
- ShadowGit App (Electron) - Handles file watching, auto-commits, UI
- MCP Server (Node.js) - Provides AI interface to git history
They communicate through a local HTTP API on port 45682:
// Session API runs locally
const SESSION_API_PORT = 45682;
// MCP server checks if session API is running
const response = await fetch(`http://localhost:${SESSION_API_PORT}/api/health`);
Smart Commit Decisions
Not every file change needs a commit. The CommitDecisionEngine is sophisticated:
shouldCommit(repoPath: string, stats: ChangeStats): boolean {
const timeSinceLastCommit = Date.now() - this.lastCommitTime;
// During AI sessions, different rules apply
if (this.hasActiveSession(repoPath)) {
return false; // Let AI control commits via checkpoint()
}
// Smart batching for regular work
if (stats.totalLines > 100 || timeSinceLastCommit > 30000) {
return true;
}
// File-type specific rules...
}
Results
The technical implementation works beautifully. Real metrics from usage:
-
Token reduction: fewer tokens when AI uses
git diff
vs reading files - Debug speed: Finding breaking changes takes seconds, not multiple prompts
- History depth: Typical repos accumulate 200-500 commits per day
What I Learned
The Hard Truth About Developer Tools
Simple defaults matter - 5-second polling feels instant, but 15 seconds feels laggy. User perception is everything.
Open source strategy - Released the MCP server open source. This killed direct monetization but created goodwill and adoption.
AI integration is the future - MCP protocol is powerful. Every developer tool should consider AI-first interfaces.
Technical Insights
Building this taught me:
- Git as a database - Using git for temporal queries is incredibly powerful
- TypeScript everywhere - Full type safety from Electron to MCP made refactoring painless
- Session complexity - Managing state between two processes (Electron + MCP) requires careful design
What Actually Matters
After building this, the key lessons:
- Performance > Features - 5-second commits beat 15-second commits with better messages
- Security first - Blocking git write operations was non-negotiable
- Local-only - Developers won't trust tools that upload code
Open Source
The MCP server is available at: https://github.com/blade47/shadowgit-mcp
Real implementation is ~2000 lines of TypeScript across multiple modules. The architecture supports:
- Multiple repositories
- Concurrent sessions
- Safe git operations
- Automatic cleanup
Would I Build It Again?
Yes, but I'd:
- Start with MCP only - The Electron app added complexity
- Focus on metrics - Track token savings, debug time reduction
Key Takeaways
- MCP protocol is production-ready - Robust enough for real applications
- Git + AI is powerful - Historical context dramatically improves AI assistance
- Complexity kills adoption - Simpler architecture would have been better
- Local-first wins - Developers value privacy over features
The code is MIT licensed. Sometimes the best outcome isn't a business, but a tool that makes a few developers' lives easier.
Technical Stack: TypeScript, Electron, MCP SDK, SQLite, Node.js
Tags: #mcp #claude #git #ai #typescript #electron
Top comments (0)