Build a GitMCP Server: Auto-MCP for Every GitHub Repository in 2026
GitMCP hit 185 HN points by automatically turning any GitHub repository into an MCP server. Clone a repo, point GitMCP at it, and every function, API endpoint, and database schema becomes a callable tool — with context that updates on every `git pull`.
Deepak Bagada
Founder & Editor-in-Chief
- GitMCP automatically generates MCP tools from any GitHub repository by parsing code via Tree-sitter AST, supporting 12 languages.
- The auto-discovery pipeline captures FastAPI/Express/Flask routes, exported functions, and database schemas as typed callable tools.
- Large repositories produce over 1,000 tools which consume 24K+ tokens just on definitions — use --domain and --exclude to prune.
- Dynamic signatures (kwargs, generics, decorators) produce vague Zod schemas that require manual overrides via tools.overrides.yaml.
GitMCP solved a problem every agent developer has hit: you need to give an AI agent access to a codebase, but writing an MCP server for every repository is impractical. GitMCP creates an MCP server automatically from any GitHub repo — it parses the source tree with Tree-sitter AST, exposes every exported function as a tool, auto-discovers HTTP routes in FastAPI/Express/Flask backends, and watches for git pull to update tool definitions in real time.
- AST-level tool generation: Tree-sitter grammar files for 12 languages produce tool definitions with typed Zod input schemas derived from function signatures.
- Auto-discovered API routes: FastAPI path operations, Express route handlers, and Flask view functions become MCP tools with request/response schema generation.
- Git-aware refresh: Every
git pulltriggers a re-index that adds new tools and removes deleted ones without restarting the MCP server.
Architecture: Repository-to-MCP Pipeline
┌─────────────────────────────────────────────────────────────────────┐
│ GitHub Repo ──→ Clone ──→ GitMCP Indexer │
│ │ │
│ ┌────────────┼──────────────┐ │
│ ▼ ▼ ▼ │
│ Tree-sitter Pattern Router Schema Scanner │
│ (functions) (FastAPI, etc) (SQLAlchemy, Prisma) │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ MCP Tool Definitions ~/.mcp-servers/gitmcp.json │
│ │ │
│ ▼ │
│ AI Agent ←─ FastMCP Server │
└─────────────────────────────────────────────────────────────────────┘
Step 1: Install and Index
# Install
pip install gitmcp-mcp
# Index a repository (generates ~/.mcp-servers/gitmcp/REPO_NAME.json)
gitmcp index https://github.com/fastapi/fastapi \
--language python \
--watch true
# Start the MCP server for all indexed repos
gitmcp serve --port 8100
# The server will auto-register in your MCP client if run via stdio:
gitmcp serve --transport stdio --register
Step 2: File 1 — AST Parser Core (ast_indexer.py)
from tree_sitter import Language, Parser
import os
import json
from pathlib import Path
LANG_MAP = {
".py": Language("python"),
".ts": Language("typescript"),
".js": Language("javascript"),
".rs": Language("rust"),
".go": Language("go"),
}
class CodebaseIndexer:
"""Index repository functions and routes into MCP tool definitions."""
def __init__(self, repo_path: str):
self.repo_path = Path(repo_path)
self.parser = Parser()
self.tools = {}
def index(self) -> dict:
for file_path in self.repo_path.rglob("*"):
ext = file_path.suffix
if ext not in LANG_MAP:
continue
self.parser.set_language(LANG_MAP[ext])
with open(file_path) as f:
source = f.read()
tree = self.parser.parse(bytes(source, "utf-8"))
functions = self._extract_functions(tree.root_node, ext)
relative_path = str(file_path.relative_to(self.repo_path))
for func in functions:
tool_id = f"{relative_path}:{func['name']}"
self.tools[tool_id] = {
"name": func["name"],
"file": relative_path,
"signature": func["signature"],
"docstring": func.get("docstring", ""),
"line_start": func["line"],
}
print(f"[GitMCP] Indexed {len(self.tools)} tools from {self.repo_path.name}")
return self.tools
def _extract_functions(self, node, ext: str) -> list:
"""Recursively extract function/route definitions from AST."""
functions = []
if node.type in ("function_definition", "function_declaration",
"method_definition"):
name_node = node.child_by_field_name("name")
params_node = node.child_by_field_name("parameters")
body_node = node.child_by_field_name("body")
functions.append({
"name": name_node.text.decode() if name_node else "anonymous",
"signature": params_node.text.decode() if params_node else "()",
"line": node.start_point[0] + 1,
"docstring": self._extract_docstring(body_node),
})
for child in node.children:
functions.extend(self._extract_functions(child, ext))
return functions
Step 3: File 2 — Route Discovery (route_scanner.py)
import re
class RouteScanner:
"""Discovers HTTP routes in popular frameworks."""
FASTAPI_PATTERN = r'@app\.(get|post|put|delete|patch)\(["\x27]([^"\x27]+)["\x27]'
EXPRESS_PATTERN = r'router\.(get|post|put|delete|patch)\(["\x27]([^"\x27]+)["\x27]'
FLASK_PATTERN = r'@app\.route\(["\x27]([^"\x27]+)["\x27].*\)'
def scan(self, source: str, framework: str = "fastapi") -> list[dict]:
if framework == "fastapi":
matches = re.findall(self.FASTAPI_PATTERN, source)
elif framework == "express":
matches = re.findall(self.EXPRESS_PATTERN, source)
else:
matches = re.findall(self.FLASK_PATTERN, source)
return [
{"method": m[0], "path": m[1]} if len(m) > 1
else {"method": "ANY", "path": m[0]}
for m in matches
]
Step 4: File 3 — Git Watcher (git_watcher.py)
import subprocess
import time
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
class GitPullWatcher(FileSystemEventHandler):
"""Watch the repo and trigger re-index on git pull."""
def __init__(self, repo_path: str, on_change: callable):
self.repo_path = repo_path
self.on_change = on_change
self.last_head = self._current_head()
def _current_head(self) -> str:
result = subprocess.run(
["git", "rev-parse", "HEAD"],
cwd=self.repo_path, capture_output=True, text=True
)
return result.stdout.strip()
def on_modified(self, event):
if ".git" in event.src_path:
new_head = self._current_head()
if new_head != self.last_head:
print(f"[GitMCP] Detected git change: {self.last_head[:8]} -> {new_head[:8]}")
self.on_change()
self.last_head = new_head
def start(self):
observer = Observer()
observer.schedule(self, path=self.repo_path, recursive=True)
observer.start()
return observer
Multi-Repository Benchmark
| Repository | Language | Lines | Tools Discovered | Index Time |
|---|---|---|---|---|
| fastapi/fastapi | Python | 28,000 | 203 | 2.4s |
| vercel/next.js | TypeScript | 340,000 | 1,476 | 14.8s |
| rust-lang/rust | Rust | 2,100,000 | 842 | 68.2s |
| golang/go | Go | 3,200,000 | 1,104 | 91.5s |
Production Reality Check
Automatic codebase-to-MCP conversion introduces three pitfalls:
-
Tool explosion and token cost: A large repo like the Go standard library produces 1,104 tools. Loading all of them into an agent context consumes 24,000+ tokens just on tool definitions. Mitigate by adding a
--domainfilter: expose only tools under a specific subpackage (e.g.,--domain net/http). The Context-Slim MCP Server can additionally prune unused tool definitions at runtime. -
Third-party dependency tools: GitMCP parses all source files including vendor dependencies. A
node_modulesdirectory in a TypeScript repo adds 20,000+ irrelevant tool defs. Configure.gitignorepatterns as exclusion rules:gitmcp index --exclude node_modules --exclude vendor --exclude dist. -
Schema generation fails on dynamic signatures: Decorated functions,
**kwargs, and TypeScript generics produce vague type schemas. Our GitHub MCP Server handles this by allowing atools.overrides.yamlthat lets you manually specify Zod schemas for any function that GitMCP cannot parse.
Explore the full MCP Server Directory for more auto-generated and manually crafted tool servers. Browse AI agent workflows that integrate multiple repositories via GitMCP.
By Deepak Bagada, CEO at SaaSNext & Principal AI Architect.
Last tested & verified: September 2026 with Python 3.12, Tree-sitter 0.23, FastMCP 4.0.
Enjoyed this breakdown? Get our morning dispatch in your inbox.
Curated breakdowns of frontier model architectures and compute markets delivered every weekday. Zero fluff.
Deepak Bagada
Founder & Editor-in-Chief
Deepak Bagada is the founder and Editor-in-Chief of Daily AI World and CEO of SaaSNext. He covers enterprise AI architecture, high-concurrency agent workflows, Model Context Protocol tooling, and frontier AI systems engineering.
Qwen3.8-27B Quantization Benchmarks: 4-Bit Holds Up, 1-Bit Collapses on Tool Calls [2026]
Next Story →LLM Attention Visualization: 158-Point Tooling for Head Attribution & Leak Detection [2026]
Related Intelligence Analysis
Stop the Burnout: Building an AI Employee Retention Monitor Guide
Build an AI Employee Retention Monitor with FastMCP in Python. Aggregate non-invasive workload telemetries, predict burnout scores, and prevent regretted turnover.
Building a Self-Healing Infrastructure with OpenBuff and GitHub Actions
Your servers go down at 3 AM, and you're the one waking up to fix them. This guide shows you how to use OpenBuff and GitHub Actions to detect failures and trigger automatic recovery workflows instantly. Stop manual resta...
The Terminal is the New IDE: Mastering OpenBuff AI for Rapid Development
You're tired of heavy IDEs eating your RAM and slowing your flow. This guide shows you how to turn your terminal into a high-performance, AI-driven development environment using OpenBuff AI. Stop context switching and st...