layergrep
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@layergrepfind the user signup handler"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
layergrep
Local, offline semantic + literal code search. Point it at a project, ask it a question in plain language ("handler that updates user status"), get back implementation locations grouped by architectural layer and module — not a flat ranked list.
Available both as a CLI and as an MCP server, so an LLM client (e.g. Claude Code) can call it directly during a conversation.
Why "layergrep" and not "codesearch"
Plain "search" implies a single list of equivalent results ranked by score.
That's not how this tool works: it's a matrix — rows are layers (a
cross-cutting architectural role: frontend, backend/api, models,
config, ...), columns are business entities/modules (a sibling
package, a Rust crate, or a feature that spans both frontend and backend
files) — and results are grouped by both, so a large module can't drown out
a smaller one just because it has more code. -grep signals "searches
code" the way ripgrep/ast-grep/semgrep do; layer is the actual
differentiator.
Related MCP server: Cortex
How it works
Chunking — AST-based (tree-sitter), not fixed-size windows. Functions, classes/structs, and methods for
.py/.js/.jsx/.ts/.tsx/.rs/.c/.h/.cpp/.cc/.cxx/.hpp/.hh/.hxx; adjacent top-level statements (constants, route registration blocks) get grouped too, so plain data/config files aren't invisible to search. JSON files (e.g. translation catalogs) get both an exact literal lookup and adaptive size-budgeted chunking for semantic search.Embedding — each chunk is embedded with a configurable sentence-transformers model (default
intfloat/multilingual-e5-small; also supportsbge-small/bge-m3/e5-large/theQwen3-Embeddingfamily). Stored in SQLite viasqlite-vec— no external vector database.Retrieval — hybrid: literal grep on exact identifiers/paths first, then per-
(layer, module)vector search (top-k within each bucket, not overall — the reason results don't collapse into a single dominant category), then two structural expansion passes: the import graph (handler → model/config/constants it imports — Python, JavaScript/ TypeScript, Rust, C, and C++, see Language support) and shared string literals across layers (language-agnostic, works everywhere — e.g. a route path used by both a JS frontend and a Python backend).Everything is project-specific and configurable, not hardcoded — layer rules, translation file paths, module depth, and noise-filtering thresholds all live in
.layergrep.json(see below).
Language support
Not every feature is at full parity across languages yet.
Feature | Python | JavaScript/TypeScript | Rust | C | C++ |
AST chunking (functions/classes/methods) | ✅ | ✅ | ✅ | ✅ | ✅ |
Layer/module classification (path & name based) | ✅ | ✅ | ✅ | ✅ | ✅ |
Cross-layer literal linking | ✅ | ✅ | ✅ | ✅ | ✅ |
Import-graph expansion ( | ✅ | ✅ | ✅ | ✅ | ✅ |
| ✅ | ✅ | ✅ | ✅ | ✅ |
| ❌ | ❌ | ✅ | ✅ | ❌ |
Install
Requires Python 3.12+. Heavy ML dependencies (torch, sentence-transformers, tree-sitter grammars) — expect a real download/install on first setup, this is an embedding-model-backed tool, not a lightweight script.
git clone <this-repo>
cd layergrep
pip install -e .Or, once published, with uv — no manual venv
needed:
uvx --from git+<this-repo-url> layergrep-mcp-serverQuickstart (CLI)
# 1. Draft a config from the target project's own structure (review it — it's a draft)
layergrep init-config --root /path/to/project
# 2. First time on this machine: download the embedding model (needs network, one-time)
layergrep install-model
# 3. Build the index (incremental — safe to re-run after code changes)
layergrep index --root /path/to/project
# 4. Search
layergrep "handler that updates user status" --root /path/to/project
# Optional: check whether the default noise-filtering thresholds fit this project's corpus size
layergrep calibrate-thresholds --root /path/to/projectlayergrep "index" (the literal word) needs the -- escape hatch, since
index alone is a subcommand: layergrep -- "index".
Quickstart (MCP server)
Add to .mcp.json in the target project:
{
"mcpServers": {
"layergrep": {
"command": "/path/to/layergrep/.venv/Scripts/python.exe",
"args": ["/path/to/layergrep/mcp_server.py"],
"env": {
"LAYERGREP_ROOT": "/path/to/target/project"
}
}
}
}(Or, if installed via pip/uv, command can just be layergrep-mcp-server
directly — no venv path needed.)
Optional env vars: LAYERGREP_SUBDIR (index only a subtree of the project,
default: the whole root) and LAYERGREP_MODEL (override the embedding
model). Three tools are exposed:
layergrep(query)— the search itself, returns markdown grouped by layer/module.index_codebase(force=False)— incremental (re)index; call after code changes.install_model()— one-time model download if the server reports it isn't cached yet (it won't crash on a fresh machine — it tells you to call this instead).
Configuration: .layergrep.json
Optional, lives at the target project's root (not inside .layergrep/,
which holds only generated artifacts — the index DB and log — and should be
gitignored). Missing entirely → everything falls into one default_layer,
which is a valid, honest result for a project that hasn't been configured
yet — never silently reuses another project's layer rules.
{
"layers": [
{"name": "frontend", "dirs": ["frontend", "client"], "files": []},
{"name": "backend/api", "dirs": ["api", "routes"], "files": ["views.py"]},
{"name": "models", "dirs": ["models"], "files": []},
{"name": "config", "dirs": ["config"], "files": ["settings.py"]}
],
"default_layer": "backend/other",
"translations": {"files": ["langs/ru.json"]},
"extra_excluded_dirs": ["vendor"],
"forced_add": ["target"],
"module_depth": 1,
"modules": [
{"name": "audio-engine", "dirs": [], "files": ["engine.rs", "Deck.tsx"]}
],
"literal_noise_threshold": 30,
"import_noise_threshold": 15
}Field | Default | Meaning |
|
|
|
|
| catch-all for anything no rule matches |
|
| JSON files to index for literal + semantic translation search |
|
| extra vendored/generated dirs to skip (bare name, or |
|
| un-excludes a bare dir name from the built-in skip list (e.g. |
|
| how many leading path components form a "module" — raise for monorepos where packages are nested (e.g. |
|
|
|
|
| max corpus-wide matches before a literal is treated as too generic to be a useful cross-layer link |
|
| max distinct importers before an import target is treated as a shared utility, not a feature-specific link |
layergrep init-config drafts this file from common Flask/Django/FastAPI/
Express/React-shaped conventions — review before relying on it, it can't
guess a project's own vocabulary. It also detects which languages are
present, and for Rust projects reads real crate names straight out of every
Cargo.toml under the root (not the directory name — a directory called
libxdo-sys-stub can hold a crate actually named libxdo-sys) to suggest
module_depth; crates aren't suggested as layers, since a crate is a
sibling package (the modules axis), not an architectural role. For
layers specifically, Rust projects get their own candidate set instead —
Cargo conventions (tests/examples/benches dirs, a Tauri app's
commands/handlers surface) and one layer per [[bin]] target read
straight from the manifest — rather than the Flask/Django-shaped
directory-naming guesses, which don't apply to Rust code at all. C has no
single dominant manifest format the way Cargo.toml is for Rust (CMake,
Makefiles, Meson, Bazel, and Autotools all coexist), so this is scoped to
CMakeLists.txt only: every add_library/add_executable call with a
literal target name becomes its own modules rule (matched by the source
files' basenames), resolved straight from the manifest rather than guessed
from directory naming — a named CMake target is a sibling deliverable
(the modules axis, same reasoning as Rust crate names above), not a
recurring architectural role. A target built from a variable name (e.g.
add_library(${LIB_NAME} ...), common in larger CMake projects) can't be
resolved this way and is skipped — a directory-naming-based layer guess
may still catch it. Same caution applies to individual source arguments:
a $<...> generator expression is dropped on its own (usually one extra
entry alongside otherwise-literal sources), but a bare ${...} variable
reference aborts the whole target instead of just that argument — real
CMake very commonly builds an entire source list into one variable first,
and keeping whatever incidental literal token happens to sit next to it
would report a confidently-wrong, badly incomplete file list rather than
correctly detecting nothing. layergrep calibrate-thresholds reports the real
match-count distribution for the
literal_noise_threshold/import_noise_threshold fields on an already-
indexed project, instead of leaving you to guess whether the defaults (tuned
on one ~8000-chunk corpus) fit yours.
Development
pip install -e ".[dev]"
pytest # full suite
pytest -m "not slow" # skip tests that load the real embedding model / spawn subprocessesOptional local git hooks (same ruff/mypy/bandit checks CI runs, catching issues before a commit instead of after a push):
pre-commit install # ruff/mypy/bandit before every commit
pre-commit install --hook-type pre-push # full (non-"slow") test suite before every pushThey run via whatever's already installed in your activated dev venv (language: system in
.pre-commit-config.yaml), not a separate managed environment — so a local run always
matches a manual ruff check ./mypy ./bandit ... invocation exactly, with nothing to
keep in sync.
Related reading
Zhang et al., "cAST: Enhancing Code Retrieval-Augmented Generation with Structural Chunking via Abstract Syntax Tree", Findings of ACL: EMNLP 2025 — arxiv.org/abs/2506.15655
Tao, Li, Qin, Liu, "Retrieval-Augmented Code Generation: A Survey with Focus on Repository-Level Approaches", Oct 2025 — arxiv.org/abs/2510.04905
Prieto-Díaz, "Implementing Faceted Classification for Software Reuse", CACM, 1991 — DOI: 10.1145/103167.103176
License
MIT — see LICENSE.
This server cannot be installed
Maintenance
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
- Alicense-qualityFmaintenanceSelf-hosted MCP server for indexing and searching code repositories via hybrid search and deep code understanding.Last updated317MIT
- Alicense-qualityDmaintenanceEnables AI coding assistants to semantically search and retrieve relevant code patterns, documentation, and implementations from a codebase via MCP tools.Last updated6MIT
- Alicense-qualityFmaintenanceMCP server for semantic code search that indexes your codebase and allows AI editors to search using natural language queries.Last updated5854MIT
- Alicense-qualityDmaintenanceEnables AI-driven semantic code search via natural language queries, integrating with MCP clients like Claude Desktop to retrieve relevant code context from any codebase.Last updatedMIT
Related MCP Connectors
Fleet discovery for the cyanheads MCP ecosystem — semantic search + install snippets.
Search public open-source code, documentation, metadata, vulnerabilities, changelogs, and examples.
Multi-engine search for AI agents. Trust scoring, local corpus, MCP-native. Self-hostable, BYOK.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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/AlexVishnivetsky/layer-grep'
If you have feedback or need assistance with the MCP directory API, please join our Discord server