Skip to main content
Glama

repo-map

A navigable map of your codebase for coding agents — so they stop reading whole files just to find their bearings.

repo-map is a Model Context Protocol (MCP) server. It parses a repository into a symbol graph, ranks every symbol by importance (a tuned PageRank), and exposes a handful of tools that let an agent like Claude Code locate code and read only the symbol it needs — instead of loading entire files to orient itself.

Think of it as an "Obsidian for your codebase": an outline + a reference graph + an importance sort, queryable without opening the files.

Why

Reading whole files to understand where a piece of logic lives is the single biggest source of wasted context in agentic coding. On a real exploration workflow over an unfamiliar Python repo, routing that navigation through repo-map instead of raw file reads measured:

Metric

Result

Total workflow token cost

−33 % (up to −52 % cold)

Context loaded into the model

−64 %

outline vs a full file read

−96 %

The gains come from never paying for a file body you don't end up editing.

Related MCP server: mcp-code-indexer

Tools

Tool

What it does

index(path)

(Re)target the server on a repo and build its map.

where_is(query, repo=None)

Find where a symbol is defined, by name (substring, case-insensitive), ranked by contextual PageRank.

grep_code(pattern, max_results=40, repo=None)

Regex search by content — each hit situated in its enclosing symbol (def/class/<module>), not a bare line.

outline(file, repo=None)

Table of contents of a file: class/function signatures + line ranges. ~95 % fewer tokens than reading it.

get_symbol(file, name, repo=None)

The full body of a single symbol — the only thing you actually read to start coding.

who_references(name, repo=None)

Who calls a symbol (what a signature change might break).

what_it_uses(name, depth=1, max_results=60, repo=None)

The mirror: what a symbol depends on. Accepts a symbol, a file (everything it defines and imports) or an exact file::symbol. depth=0 walks the full transitive closure — all the code actually reached from an entry point.

refresh(repo=None)

Rebuild a target's map (incrementally) without moving the default target — use it after editing files.

One repo per call (repo=)

Every read tool takes an optional repo= (always the last parameter, so existing positional calls keep working). It sets the target for that single call:

index("/work/backend")          # default target for this server
outline("api/routes.py")        # → /work/backend
outline("src/App.tsx", repo="/work/dashboard")   # → /work/dashboard, built on the fly
where_is("Router")              # → still /work/backend

Why it matters: the server is started once per session and several agents can query it in parallel on different repos. Each target keeps its own graph and its own ranking context (recently touched files / mentioned symbols), so one agent's exploration can't silently answer another agent's question with the wrong repo's map. A few targets are kept in memory at once (LRU, see GRAPHS_CAP); an evicted one is rebuilt from the on-disk cache when it comes back — or fully rebuilt if that cache is missing or stale.

A map does not refresh itself: once built, a target's map stays as it is until something rebuilds it. index(path) rebuilds, but it also moves the default target — which would steal it from another agent. Use refresh(repo=...) to rebuild a target and nothing else:

refresh(repo="/work/dashboard")   # rebuilt (incrementally); default target untouched

index(path) still sets the default target — the one used when repo is omitted — and resets the ranking context of that target only: it no longer wipes the graph or the session state of the other targets. Calling a tool without repo behaves exactly as it always did.

Languages

Python, JavaScript/JSX, TypeScript/TSX — via precompiled tree-sitter grammars (no C toolchain required, including on Windows).

Install

git clone https://github.com/noambinabout-boop/repo-map.git
cd repo-map
python -m venv .venv
# Windows:  .venv\Scripts\activate
# Unix:     source .venv/bin/activate
pip install -r requirements.txt   # or: uv pip install -r requirements.txt

Wire it into Claude Code

Register the server (adjust the paths to your clone):

claude mcp add repo-map -- /path/to/repo-map/.venv/bin/python /path/to/repo-map/server.py

Then, from any session:

index("/path/to/the/project/you/want/to/explore")
where_is("MyClass")
outline("src/app.py")
get_symbol("src/app.py", "MyClass")

The server also runs standalone over stdio (python server.py) for any MCP-compatible client.

How it works

  • Symbol graph. tree-sitter parses each file into definitions and a call/reference graph.

  • Importance ranking. A PageRank variant weighted against popularity (1/fan-in) so ubiquitous helpers don't drown out the code that actually structures the repo, merged with the import graph so shared components/constants aren't invisible.

  • Scope resolution. self/cls/this, class inheritance (Python & JS/TS extends), named/namespace/default imports, light type inference (x = Ctor(); x.foo()Ctor.foo) and lexical scope (a call to a name the calling file defines itself resolves to that local definition, not to same-named functions elsewhere) are resolved to the right target. Resolution stays conservative: when a target is ambiguous, it falls back to a broad edge rather than dropping one.

  • Path aliases. compilerOptions.paths / baseUrl from tsconfig.json / jsconfig.json are honored (JSON-with-comments tolerated), so @/lib/db resolves like a relative import. This is the default import style on Next/Vite: on a 285-file Next app, 532 of 601 internal imports go through @/, and reading them doubled the import graph (3 135 → 6 745 edges). Fail-open — an unreadable or exotic tsconfig just means no aliases.

  • Incremental cache. Parses are cached per file by mtime in ~/.repo-map/cache/ (override with REPO_MAP_CACHE). Nothing is written into the repos you target. First index of a 284-file TS project: ~17 s; subsequent: ~0.3 s.

  • Per-repo ignores. Drop a .repomapignore (.gitignore syntax) at a repo root to keep generated/vendored dirs out of the graph.

Limitations (honest)

  • PageRank is sharper on Python than on React/Expo. The import-graph merge fixes "invisible shared components," but entry-point screens referenced only once by a route table can still rank low.

  • Edges mean "uses", not just "calls" (JS/TS). Since 2026-08-19 the graph also reads JSX tags (<PatientCard />), identifiers passed as props (onClick={handleDelete}) and methods called on an imported object (db.query(...) links to db, the symbol that actually crosses the file boundary). Measured on a 208-file Next.js app: unreachable symbols dropped from 571 to 442, and screen components never reached by anyone from 113 to 7. Lowercase JSX tags (<div>) are skipped — that is JSX's own rule for native elements. Python is untouched.

  • Name-based resolution outside the cases above may add a spurious edge. The single place where edges are deliberately removed is lexical scope: a call is no longer linked to a same-named definition in another file when the calling file defines that name itself. Measured on a 285-file TS app: 59 edges out of 2006 dropped, all of them cross-file homonyms (a main() per CLI script, a load() per React component), and no caller left disconnected from the symbol it calls. Methods are excluded from this rule — reaching them requires self./this., handled separately.

  • Python from . import x (no module name) is not resolved.

  • repo-map indexes structure, not literals — use grep_code for flags/config strings.

Tests

./.venv/Scripts/python.exe tests/run_tests.py

A non-regression suite of fixtures freezes each scope-resolution feature and compares the exact set of graph edges. It also covers target isolation: two repos alive in one process, same-named files, default target, ranking context and LRU eviction.

Prior art

The "repo map" idea was pioneered by Aider. repo-map is an independent MCP implementation with its own ranking, conservative scope resolution, and per-file incremental cache.

License

MIT

A
license - permissive license
Not graded
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • A
    license
    A
    quality
    D
    maintenance
    Provides coding agents with a mental map of codebases via progressive disclosure, enabling efficient exploration of project structure and entity relationships.
    3
    6
    GPL 3.0
  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables AI agents to intelligently navigate and understand codebases by providing instant file descriptions, semantic search, and context-aware recommendations, eliminating the need to repeatedly scan files.
    18
    MIT
  • A
    license
    A
    quality
    B
    maintenance
    Enables LLMs to efficiently navigate large codebases by providing surgical access to specific code symbols via semantic search and call-graph queries.
    6
    MIT

View all related MCP servers

Related MCP Connectors

  • Give your AI agent a persistent map of your project's structure, dependencies, and bugs.

  • Code intelligence for coding agents: semantic, AST, graph, and full-text search. 279+ languages.

  • One shared brain for your AI coding agents: team memory, agent Q&A, tasks, and file claims.

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/noambinabout-boop/repo-map'

If you have feedback or need assistance with the MCP directory API, please join our Discord server