WRITING July 22, 2026 8 min read

Building an MCP Server for Git Intelligence: How I Built Lore

Git data is rich but unstructured. Lore is an MCP server that pre-indexes blame, history, and co-change patterns into BadgerDB and serves them as seven focused tools, giving agents structured git intelligence instead of raw CLI output.

Claude Code runs git log, git blame, git show as separate shell commands, each returning unstructured text the agent has to parse. The problem isn’t that git is slow. It’s that the agent is using git as a query engine, and git isn’t a query engine. Git is a content-addressable filesystem with a porcelain layer designed for humans reading terminals. When an agent needs to know who last touched line 47 of a file and why, it runs git blame, gets back a wall of text with commit hashes and dates and author emails jumbled together, and then has to extract the answer from that output. Multiply this by every question an agent asks about history, authorship, and change patterns, and you’re burning context window on parsing overhead.

Lore is my answer to this. It’s a Go daemon that pre-indexes git blame and commit history into BadgerDB and serves structured results over MCP. Seven tools, each answering one question about a repository’s history. No parsing required on the agent’s side.

What agents actually need from git

When I watched how Claude Code uses git data, the same questions kept coming up:

Blame per line. Who wrote this line, when, and in what commit? The agent needs this when it’s debugging or trying to understand intent. Raw git blame output is a nightmare to parse programmatically. The porcelain format is better but still requires non-trivial extraction.

History per file. What changed recently in this file? The agent needs this for context when making modifications. It wants to know whether a file is actively being worked on, what direction it’s moving in, and whether its planned change conflicts with recent work.

Co-change patterns. What files tend to change together? This is the question git fundamentally cannot answer in a single command. You’d need to iterate over commits, build a co-occurrence matrix, and filter noise. No agent is going to do that in a tool call.

Hotspots. Where is the most churn in the repository? High-churn files are where bugs live, where complexity accumulates, and where an agent should be most careful.

Each of these questions maps directly to a tool. That’s the design principle: one question, one tool, one structured response.

Seven tools, each doing one thing

Lore exposes seven MCP tools. No resource primitives, no prompt templates, just tools, because every interaction is a query with parameters and a typed response.

lore_blame returns structured blame data for a file: line number, author, commit hash, date, and commit message for each line or range. The agent asks “who wrote lines 40-60 of this file?” and gets back a clean array it can reason over immediately.

lore_history returns recent commits affecting a specific file, ordered newest-first. Commit hash, author, date, message, and the diff summary. The agent asks “what happened to this file recently?” and gets a timeline.

lore_contributors returns ranked authorship for a file or directory: who has written the most code that still exists. Useful for the agent to know who to attribute knowledge to, or to understand ownership patterns.

lore_hotspots returns the most-churned files in the repository, ranked by change frequency. The agent asks “where is the action?” and gets a prioritized list.

lore_status reports daemon state and index metadata: how many commits are indexed, when the last reindex happened, which repository is loaded. Diagnostic plumbing.

The two most interesting tools are the ones that have no equivalent in the git CLI:

lore_cochange takes a file path and returns other files that frequently change alongside it, ranked by co-occurrence score. This is computed from commit history: if two files appear in the same commit consistently, they’re coupled. More on this below.

lore_intent combines blame data with commit messages to answer “why is this code the way it is?” You point it at a line or range, and it returns who wrote it, when, and the full commit message explaining the change. The commit message is the closest thing to intent documentation that exists in most codebases.

Co-change is the query you can’t get from code

Coupling in real codebases is often historical, not structural. Two files with no import relationship change together 80% of the time because they’re connected through a queue, an event, or a convention. The handler file and the test file. The migration and the model. The API route and the frontend component that calls it. The config file and the three services that read it.

The agent can’t discover this by reading code. Static analysis will find import graphs and call chains. It won’t find that every time someone touches pricing.go they also touch billing_test.go and stripe_webhook.go. That pattern only exists in history.

Lore builds a co-occurrence matrix from commit data. For every file in every commit, it records which other files appeared in the same commit. After processing the index, it can answer “what changes with X?” in sub-millisecond time. The one nuance is filtering: commits touching 50+ files are excluded from the co-change calculation. Bulk refactors, formatting passes, and dependency updates would otherwise pollute the signal. A commit that touches every Go file because someone ran gofmt tells you nothing about logical coupling.

This is the tool I use most through Lore. When an agent is about to modify a file, knowing what else typically changes alongside it is the difference between a complete change and a change that breaks something downstream.

Lore_intent: the “why” behind a line

Most code doesn’t have comments explaining why it exists. But most code does have a commit message from when it was written. The commit message is the closest thing to a “why” annotation that exists at scale.

lore_intent takes a file and a line range, runs blame to find which commits introduced those lines, then returns the full commit context: message, author, date, and the other files that were part of that commit. The agent gets both “who wrote this” and “what were they trying to accomplish.”

This turns out to be one of the most useful tools for debugging. When the agent encounters code that looks wrong or confusing, it can ask for intent and get back the original commit message. Often that message explains a constraint, a workaround, or a dependency that isn’t obvious from the code alone. “Fixed race condition when queue consumer reconnects” explains a lot about why a retry loop has a specific backoff pattern.

Indexing strategy

Lore indexes the last 500 commits. This is a deliberate bound. Empirically, anything beyond 500 commits has diminishing returns for the kinds of questions agents ask. Recent history is what matters: who touched this file last week, what’s been churning this month, what changed alongside this file in the last few months. Ancient history rarely affects the agent’s immediate decisions, and indexing the full history of a large repository would make startup time unacceptable.

The indexing pass shells out to git blame --porcelain rather than using go-git or any other Go-native git implementation. The subprocess overhead doesn’t matter for a one-time indexing pass that runs at startup and on reindex. What matters is correctness and compatibility: git blame --porcelain has been stable for decades and handles every edge case (renames, copies, binary files) that a library implementation might not.

For storage, Lore uses BadgerDB with a descending-key trick for time-ordered data. Keys are prefixed with MaxInt64 - timestamp, which means a simple forward iteration in BadgerDB returns results newest-first. This avoids reverse iteration (which is slower in LSM-tree databases) and means the most common query pattern, “give me the N most recent items,” is a simple prefix scan that stops after N results.

Watching .git, not the source tree

Lore needs to know when new commits land so it can reindex. The naive approach is watching the entire source tree with fsnotify and triggering on file changes. This is wasteful and noisy: file saves, editor swap files, build artifacts, all generating events that don’t mean a new commit happened.

Instead, Lore watches exactly two paths: .git/refs/heads/ and .git/HEAD. A new commit on any branch updates a ref under .git/refs/heads/, and a branch switch updates .git/HEAD, with nothing else to watch. Two paths instead of thousands, and zero false positives. When either path changes, Lore diffs its indexed state against the new HEAD and indexes any new commits.

This means Lore can sit idle consuming nearly zero CPU while you edit files, and only wake up to do work when you actually commit. For a daemon that runs permanently in the background, this matters.


Lore is one piece of a broader approach to giving agents structured access to development context. If you’re interested in the protocol layer, I wrote a full guide to MCP servers. For the Go-specific implementation patterns (stdio transport, BadgerDB indexing, daemon lifecycle), see Building MCP Servers in Go. And for the architectural argument about why agents need pre-computed context at all, see The Context Layer.

Frequently Asked Questions

What is a git MCP server?

A git MCP server is a program that exposes structured git data (blame, history, co-change patterns, hotspots) to AI agents via the Model Context Protocol. Instead of the agent running raw git commands and parsing text output, the MCP server pre-indexes git data and serves it as typed, queryable tools.

How do git intelligence tools help AI agents?

AI agents frequently need to understand code authorship, change history, and file relationships. Git intelligence tools answer questions like 'who wrote this line and why,' 'what files always change together,' and 'where is the most churn,' all queries that are expensive or impossible to answer with raw git commands alone.

Can an MCP server replace git commands for AI agents?

An MCP server doesn't replace git: it sits on top of it. The server indexes git data into a queryable store and exposes structured tools that return typed results. The agent gets pre-computed answers to common questions instead of having to run multiple git commands and parse their text output.

What is co-change analysis in git history?

Co-change analysis identifies files that tend to change together across commits. Two files might have no import relationship but change together 80% of the time because they're connected through a queue, event, or convention. This coupling is invisible in the code but clear in commit history.

Back to all writing