Server data from the Official MCP Registry
Local-first SCIP code navigation and Zoekt search over your own indexed repositories
About
Local-first SCIP code navigation and Zoekt search over your own indexed repositories
Security Report
Valid MCP server (1 strong, 0 medium validity signals). No known CVEs in dependencies. Package registry verified. Imported from the Official MCP Registry.
3 files analyzed · 1 issue found
Security scores are indicators to help you make informed decisions, not guarantees. Always review permissions before connecting any MCP server.
Permissions Required
This plugin requests these system permissions. Most are normal for its category.
What You'll Need
Set these up before or after installing:
Environment variable: CODEINTEL_DATA_DIR
How to Install
Add this to your MCP configuration file:
{
"mcpServers": {
"io-github-phuongddx-codeintel": {
"args": [
"codeintel-navigation-mcp"
],
"command": "uvx"
}
}
}Documentation
View on GitHubFrom the project's GitHub README.
codeintel
Local-first code intelligence for coding agents. Precomputed SCIP navigation (go-to-definition, find-references, call hierarchy, document symbols), Zoekt lexical search, cross-repo blast radius, and semantic search — exposed as MCP tools to Claude Code, Cursor, or any MCP client.
Runs as a single stdio process reading local SQLite files. No server, no auth, no network, nothing leaves your machine.
Quick Start
# 1. External indexer binaries (scip, zoekt, per-language indexers)
curl -fsSL https://raw.githubusercontent.com/phuongddx/codeintel/main/setup.sh | sh
# 2. codeintel itself
uv tool install codeintel-navigation-mcp
# 3. Index a repo (slug defaults to the directory name)
codeintel index /path/to/your/repo
4. Register the MCP server. Using Claude Code, install the plugin and it registers itself:
/plugin marketplace add phuongddx/codeintel
/plugin install codeintel@codeintel
Any other MCP client (or Claude Code without the plugin) registers manually:
claude mcp add codeintel --scope user -- codeintel-server
That's it — ask your agent "find all references to AuthService" and it will
call findReferences instead of grepping.
{
"mcpServers": {
"codeintel": {
"command": "codeintel-server"
}
}
}
If your client can't find codeintel-server on PATH (GUI apps often don't
inherit your shell's), use the absolute path from which codeintel-server.
git clone https://github.com/phuongddx/codeintel && cd codeintel
uv sync
claude mcp add codeintel --scope user -- uv --directory "$(pwd)" run codeintel-server
MCP tools
| Tool | What it does |
|---|---|
goToDefinition | Resolve a symbol to its defining file and range |
findReferences | Every occurrence of a symbol across the indexed repo |
callHierarchy | Incoming/outgoing calls for a symbol |
documentSymbols | Outline of every symbol defined in one file |
searchCode | Zoekt lexical/regex search, optionally filtered to one repo |
semanticSearch | Natural-language search — vector hits fused with Zoekt via reciprocal rank fusion |
blastRadius | Which other indexed repos depend on a package, up to 2 hops |
getIndexStatus | Published commit, freshness, staleness vs. a working tree |
typeHierarchy | Supertypes/subtypes — currently non-functional, see limitations |
Every nav tool takes repo (the slug from codeintel index) plus a
tool-specific symbol or path. All tools report failure the same way — a
{"error": "..."} payload rather than a transport-level error, so a query bug
never kills the stdio server.
Requirements and limits
Read this before installing — codeintel is deliberately narrow.
-
macOS and Linux only. Windows is not supported.
-
One language per repo. Language is detected by extension plurality across git-tracked files; a polyglot monorepo gets indexed as whichever language has the most files. Multi-language merge is out of scope. Override with
--language. -
Four language families: TypeScript/TSX, Python, Java/Kotlin, Swift. Rust, Go, C/C++, C#, Ruby, and PHP are not supported.
-
Navigation and search only — codeintel never edits code. If you want an agent that can perform semantic renames and refactors, you want Serena; the two are complementary.
-
Indexing is a separate, explicit step. Nothing is live-analyzed. Run
codeintel index(orcodeintel watch) to publish an index before querying. -
Requires external binaries that
setup.shinstalls:Purpose Binary Source SCIP → SQLite conversion scipprebuilt, pinned v0.9.0(minimum — older versions silently drop occurrence ranges)Lexical search zoekt-index·zoekt-webservercross-compiled by our CI — upstream publishes no binaries TypeScript indexing scip-typescriptnpm install -gPython indexing scip-pythonnpm install -gSwift indexing scip-swiftprebuilt, macOS arm64 only Java/Kotlin indexing scip-javadetect-only — Docker image, asks before pulling Options:
--only <name>to install one dependency,--forceto reinstall,--helpfor usage. Re-running is safe: anything already present is skipped.
Optional extras:
uv tool install "codeintel-navigation-mcp[watch]" # + watchdog, for `codeintel watch`
uv tool install "codeintel-navigation-mcp[semantic]" # + lancedb/sentence-transformers/tree-sitter, for semanticSearch
Why it's built this way
Overview — client, server, the 3 engines (Query / Search / Graph), and storage:

Index pipeline & package graph — how codeintel index builds and publishes
an index, how the package dependency graph feeds blastRadius, and how
codeintel watch debounces a burst of edits into one reindex:

Three things worth reading the diagrams for:
- The runtime path never writes. Queries open a published
index-<sha>.dbread-only (mode=ro&immutable=1). Index files are never mutated in place. - Publishing is atomic. A reindex writes a new versioned
.db, populates the package graph, and runszoekt-index— only once all of that succeeds doesos.replace(POSIXrename(2)) flip the smallcurrentpointer. A query already reading the old file keeps working; there is no downtime window, and a failure anywhere leaves the previously published index live. - The package graph is rebuilt, not accumulated. Each reindex clears that
repo's own outgoing edges before recomputing them, so a removed dependency's
edge is retracted —
blastRadiusalways reflects each repo's last index run.
Editable sources:
docs/assets/codeintel-architecture.excalidraw ·
docs/assets/codeintel-system-architecture.excalidraw
Core query/search logic is ported from an internal reference implementation; the enterprise shell (FastAPI, Postgres, hosted-git auth, Cloud Build) is dropped in favor of a single stdio process reading local SQLite files.
Indexing a repo
codeintel index /path/to/your/repo # slug defaults to the directory name
codeintel index /path/to/your/repo --slug foo # or pick one explicitly
codeintel index /path/to/your/repo --scheme MyScheme # Swift repo with an ambiguous Xcode scheme
codeintel index /path/to/your/repo --language python # force the language instead of detecting it from git-tracked files
codeintel index /path/to/your/repo --semantic-include vendor/generated # force-include a path the generated-file filter would otherwise skip
codeintel list
codeintel status foo
codeintel reindex foo
codeintel forget foo
status (as shown by both list and status) is usually indexed or
failed, but can also be partial: the index published real symbols but no
navigable positions (an indexer/converter bug) — check the stderr warning
from codeintel index for details.
--semantic-include is repeatable — pass it once per path prefix to
force-include several. Like --scheme and --language, once set there is no flag to clear
it; change it by re-running codeintel index with the new value(s).
Language detection counts source files by extension across git-tracked files and picks the winner — one language per index:
| Extensions | Indexer |
|---|---|
.ts .tsx | scip-typescript |
.py | scip-python |
.java .kt | scip-java |
.swift | scip-swift |
Ties break by fixed priority (.ts → .tsx → .py → .java → .kt → .swift).
.git, node_modules, .venv, __pycache__, dist, and build are
skipped. Reading git rather than walking the filesystem is deliberate: a walk
also counts gitignored scratch directories, which can outnumber a repo's own
code and pick a language it doesn't use.
The pipeline then runs: chosen indexer → scip expt-convert → populate the
package dependency graph (packages/edges tables in registry.db) →
zoekt-index into ~/.codeintel/.zoekt → copy to
~/.codeintel/scip/_/<slug>/_/index-<sha>.db → atomic current pointer flip →
registry update.
The
scip/_/<slug>/_/path shape reuses the vendoredIndexConnectionCache's(project, repo, branch)3-tuple layout with the outer two pinned to_(seesrc/codeintel/config.py). It is not a user-facing contract — only<slug>matters when calling tools.
Swift indexing works end-to-end. It requires scip >= v0.9.0: older converters
cannot read scip.proto's typed_range oneof, which is the only range encoding
scip-swift emits, and silently produce an index with no navigable positions.
codeintel index refuses an older scip rather than publishing one.
Indexing a Swift repo with code-signed app-extension targets additionally requires
scip-swift >= v0.1.2: earlier versions pass no code-signing overrides to xcodebuild, which
then fails provisioning for every signed target before compiling anything. Because setup.sh
skips any dependency that is merely present, an existing install is not upgraded by
re-running it — use sh ./setup.sh --only scip-swift --force.
Watching a repo (auto-reindex)
codeintel watch /path/to/your/repo # debounce defaults to 5s
codeintel watch /path/to/your/repo --debounce 3
codeintel watch /path/to/your/repo --scheme MyScheme
codeintel watch /path/to/your/repo --language python
Runs in the foreground (not a daemon) using watchdog — install it with the
watch extra. A burst of file changes (e.g. an editor's atomic save touching
several files) coalesces into exactly one reindex. The reindex fires once
--debounce seconds (default 5) have passed since the last file change —
this prevents thrashing on rapid edits. .git, node_modules, .venv,
__pycache__, dist, and build are ignored.
Tool details
getIndexStatustakes an optionalrepo_path(the repo's local git working directory) to compare the published commit againstgit rev-parse HEAD. Omitted, freshness is reported without a staleness check — neverstale: truewithout evidence.searchCodetakesqueryplus an optionalrepofilter. On first call it lazy-spawns an embeddedzoekt-webserver(pidfile'd so a second codeintel process reuses it instead of spawning a duplicate; killed on clean exit viaatexit).blastRadiustakesrepoplussymbol_or_package(e.g."npm:@scope/ name", the same"{manager}:{name}"stringcodeintel indexderives from each repo's SCIP symbols). Returns every other indexed repo whose package depends on it, up to 2 hops, each tagged with its hop distance. The package graph has no per-node timestamp, sofreshnessis always"unknown"here — an honest limitation of the schema, not a bug. Cross-repo edges resolve by exact package name against whatever has already been indexed: index the dependency first, or re-runcodeintel index/reindexafter indexing it, for an edge to appear. Each reindex retracts that repo's own stale edges before recomputing them, so a removed dependency's edge disappears too — the graph always reflects each repo's last index run, not an accumulation of every run it's ever had.semanticSearchtakesrepoplus a natural-languagequery. Requires the optionalsemanticextra. Results fuse a LanceDB vector search over tree-sitter-chunked code withsearchCode's Zoekt hits via reciprocal rank fusion. Raises a clear error if the repo has never been indexed with the extra installed (codeintel reindex <slug>after installing it builds the missing table); indexing itself is non-fatal — a failure there never blocks the rest ofcodeintel index. Semantic indexing also respects.gitignore(on top of the hardcoded ignore-directory list) and skips any file over 1 MB, in addition to the existing generated-file banner/long-line detection —--semantic-includeoverrides all three.
Known upstream limitations
These are real behaviors of scip expt-convert (as of v0.9.0), not codeintel bugs:
typeHierarchyreturns an explicit{"error": ...}, not empty arrays, on every real-world index — the converter declaresglobal_symbols.relationshipsin its schema but never writes it. An empty result would wrongly assert "no supertypes"; the error says "cannot tell" instead. Reported upstream: scip-code/scip#464, fixed by scip-code/scip#465 (open, CI green).displayName/kindare oftennullfor symbols the converter only ever sees as bare occurrences (no definingSymbolInformationwas indexed).searchCode'srepofilter matches Zoekt's own repository name, whichcodeintel indexnow names after the slug viazoekt-index -meta— so this no longer diverges for repos indexed with current code. Shards published by an older codeintel still carry their old directory-derived name until youcodeintel reindex <slug>.
Configuration
Data directory (default ~/.codeintel):
CODEINTEL_DATA_DIR=/custom/path codeintel index /path/to/repo
Environment variables:
CODEINTEL_DATA_DIR— override default~/.codeintelfor all indexes and registryCODEINTEL_EMBEDDING_QUERY_PREFIX/CODEINTEL_EMBEDDING_DOC_PREFIX— override the query/document instruction prefix applied before embedding. Auto-detected for bge-m3, e5, and nomic-embed; set these if using a different model that needs one —semanticSearchwarns when an unlisted model has no prefix configured.
Agent skills
Three agent skills ship in the Claude Code plugin, under plugin/skills/:
codeintel-setup— install, register, index, verify.codeintel-use— prefer codeintel for structural queries (find references, go-to-definition, hierarchy).codeintel-issues— file codeintel bugs/features viagh.
Install them, and register the MCP server, with:
/plugin marketplace add phuongddx/codeintel
/plugin install codeintel@codeintel
See Quick Start above for the manual registration alternative.
Standards
Blob decoding follows the SCIP protocol:
scip_pb2.py is generated from scip.proto at scip-code/scip tag
v0.9.0 (regenerated up from v0.7.0, which lacked the typed_range oneof
scip-swift requires), and occurrence/relationship blobs are decoded as real
scip.Document / scip.SymbolInformation messages.
The SQLite layer (documents, chunks, global_symbols, mentions,
defn_enclosing_ranges) is not part of that published spec — it is the
output shape of the experimental scip expt-convert sub-command, verified by
hand against a real index. Treat it as a moving target across scip releases.
Tests
uv run pytest
Integration tests that shell out to the real scip-python / scip /
zoekt-index binaries are marked integration:
uv run pytest -m "not integration" # unit only
uv run pytest -m integration # real-binary pipeline
Documentation
docs/project-overview-pdr.md— scope, value prop, out-of-scope itemsdocs/system-architecture.md— architectural guarantees, storage layout, query pathsdocs/codebase-summary.md— module map, test coveragedocs/code-standards.md— code patterns and conventionsdocs/project-roadmap.md— all phases complete, future ideas
All 4 planned phases are shipped — see
plans/0724-2316-codeintel-mcp-implementation/plan.md.
License
Reviews
No reviews yet
Be the first to review this server!
More Developer Tools MCP Servers
Fetch
Freeby Modelcontextprotocol · Developer Tools
Web content fetching and conversion for efficient LLM usage
Git
Freeby Modelcontextprotocol · Developer Tools
Read, search, and manipulate Git repositories programmatically
Toleno
Freeby Toleno · Developer Tools
Toleno Network MCP Server — Manage your Toleno mining account with Claude AI using natural language.
mcp-creator-python
Freeby mcp-marketplace · Developer Tools
Create, build, and publish Python MCP servers to PyPI — conversationally.
MarkItDown
Freeby Microsoft · Content & Media
Convert files (PDF, Word, Excel, images, audio) to Markdown for LLM consumption
MCP Marketplace
Freeby mcp-marketplace · Developer Tools
Search and install MCP servers from inside your AI client.
