# Lgrep

> Dual-engine code intelligence for OpenCode: semantic code search plus symbol lookup for AI agents.

- **Type:** MCP server
- **Install:** `agentstack add mcp-sharper-flow-lgrep`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [Sharper-Flow](https://agentstack.voostack.com/s/sharper-flow)
- **Installs:** 0
- **Category:** [Search](https://agentstack.voostack.com/c/search)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [Sharper-Flow](https://github.com/Sharper-Flow)
- **Source:** https://github.com/Sharper-Flow/lgrep
- **Website:** https://sharperflow.com/projects/lgrep

## Install

```sh
agentstack add mcp-sharper-flow-lgrep
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

lgrep

  Local-first code intelligence for OpenCode

  
    
  

  
  
  

  Project Page
  &middot;
  GitHub
  &middot;
  Changelog

---

`lgrep` gives AI agents a better first move in codebases they already have on disk.

It is built for active local development, where files are changing, multiple agent sessions may be exploring the same repo, and the first challenge is often finding the right implementation before anyone knows the right symbol name.

Instead of starting with `glob`, `grep`, and random file reads, agents can:

- search by meaning when they do not know the symbol yet
- search by symbol when they know the name
- inspect file and repo structure before opening code
- reuse one warm local server across multiple sessions and agents

That is the whole pitch: fewer bad searches, less wasted context, faster understanding for both humans and agents working in local repos.

## What lgrep is

`lgrep` combines two complementary engines in one MCP server for local repositories:

- **Semantic engine** - natural-language code search using Voyage Code 3 embeddings with local LanceDB storage
- **Symbol engine** - exact symbol, outline, and text tools using tree-sitter parsing with a local JSON index

Use the semantic engine to answer questions like:

- "where is auth enforced between route and service?"
- "how does retry logic work for failed requests?"
- "where are permissions checked?"

Use the symbol engine to answer questions like:

- "find the `authenticate` function"
- "show me the outline for `src/auth.py`"
- "get the symbol source for `UserService.login`"

Your working tree stays local and searchable as it evolves. Only short semantic queries and indexing payloads go to Voyage. Symbol lookup stays fully local and works without an API key.

## Why it exists

AI coding agents usually fail early, not late.

They miss because they start with the wrong retrieval primitive:

- `grep` cannot answer concept questions
- full-file reads waste tokens on irrelevant code
- repeated local exploration across parallel agents duplicates work

`lgrep` fixes that by giving agents a search stack that matches how they actually reason:

1. find the implementation by intent
2. narrow to the right file or symbol
3. retrieve only the code that matters

For heavy OpenCode users, this is not a convenience plugin. It is search infrastructure.

## Why lgrep feels different

- **Intent-first search** - agents can ask by meaning before they know names
- **Exact structure tools** - file outlines, repo outlines, symbol lookup, and text search are in the same server
- **Local-first storage** - vectors and indexes live on disk, not in someone else's SaaS
- **Shared warm process** - one HTTP MCP server can serve multiple concurrent OpenCode sessions against the same local repos
- **Commercially usable** - `lgrep` is MIT licensed, so commercial use is allowed

## Comparisons

### lgrep vs grep and ripgrep

`grep` and `rg` are still the right tool for exact text and regex lookups. They are not good at intent discovery.

If the code says `jwt.verify()` and your agent asks "where is authentication enforced?", text search often misses the right entry point. Semantic search closes that gap.

### lgrep vs mgrep

[`mgrep`](https://github.com/mixedbread-ai/mgrep) is the closest semantic-search comparison point.

- `mgrep` is semantic-only
- `lgrep` combines semantic search with symbol and structure tools
- `mgrep` is cloud-oriented
- `lgrep` keeps vectors local and shares one warm server across agents

### Best-fit workflow

`lgrep` is strongest when an agent is already inside a local repo and needs to move from intent to source without wasting context:

1. ask by meaning when the right name is unknown
2. inspect the matching files and outlines
3. retrieve the exact symbol or text that matters
4. keep the same warm local server available as the working tree changes

## How it works

### Architecture

```mermaid
flowchart LR
    A[OpenCode Session 1] --> M[lgrep MCP Server]
    B[OpenCode Session 2] --> M
    C[OpenCode Session N] --> M

    M --> S[Semantic Engine\nVoyage Code 3 + LanceDB]
    M --> Y[Symbol Engine\ntree-sitter + JSON index]

    S --> V[(Local vector store)]
    Y --> J[(Local symbol store)]
    S -. query embeddings .-> Q[Voyage API]
```

```text
Agent -> lgrep MCP server -> semantic engine + symbol engine
```

### Semantic engine

1. Discover files while respecting `.gitignore`
2. Chunk code with AST-aware boundaries
3. Embed chunks with Voyage Code 3
4. Store vectors locally in LanceDB
5. Search with hybrid retrieval and reranking

### Symbol engine

1. Parse source with tree-sitter
2. Extract functions, classes, methods, and related structure
3. Store a local symbol index
4. Serve symbol search, outlines, and source retrieval without an API call

## Installation

### Requirements

- Python 3.11+
- a Voyage API key if you want semantic search

### Install from GitHub

```bash
pip install git+https://github.com/Sharper-Flow/lgrep.git
```

### Install from source

```bash
git clone https://github.com/Sharper-Flow/lgrep.git
cd lgrep
pip install .
```

## Fast setup for OpenCode

**stdio is the local default** for single-session / single-user setups — no server process needed. For shared or multi-session deployments, see [Scale-up: shared HTTP server](#3-scale-up-shared-http-server) below.

### 1. Get a Voyage API key

Create a key at [dash.voyageai.com](https://dash.voyageai.com/).

You only need this for the semantic engine. The symbol engine works without it.

### 2. Wire it into OpenCode

For single-user, single-session setups, stdio is the local default. Add this to `~/.config/opencode/opencode.json`:

```json
{
  "instructions": [
    "~/.config/opencode/instructions/lgrep-tools.md"
  ],
  "mcp": {
    "lgrep": { "type": "local" }
  }
}
```

If you prefer to run a shared HTTP server (see [section 3](#3-scale-up-shared-http-server)), swap the `mcp.lgrep` block for:

```json
{
  "mcp": {
    "lgrep": {
      "type": "remote",
      "url": "http://localhost:6285/mcp",
      "enabled": true
    }
  }
}
```

Or let the installer wire the shared-HTTP path automatically:

```bash
lgrep install-opencode
```

That installer will:

- create `~/.cache/lgrep/` for indexes and logs
- add a `type: "remote"` MCP entry pointing at `http://localhost:6285/mcp`
- copy the packaged `lgrep-tools.md` instruction and `skills/lgrep/SKILL.md` into your OpenCode config
- append the instruction file to the `instructions` array so agents prefer `lgrep` first

To use stdio with `lgrep install-opencode`, run the installer first and then change `mcp.lgrep` in `opencode.json` to `{ "type": "local" }`.

Important: the active agent must also expose `lgrep_*` tool definitions in its
tool manifest. If an agent profile only allows `read`/`glob`/`grep`, the model
cannot choose lgrep even when the MCP server is configured and the instruction
policy is present.

The installed files land at:

- `~/.config/opencode/instructions/lgrep-tools.md`
- `~/.config/opencode/skills/lgrep/SKILL.md`

### 3. Scale-up: shared HTTP server

For shared or multi-session deployments, run lgrep as a persistent HTTP server instead of stdio:

```bash
VOYAGE_API_KEY=your-key \
LGREP_WARM_PATHS=/path/to/project-a:/path/to/project-b \
lgrep --transport streamable-http --host 127.0.0.1 --port 6285
```

Why HTTP instead of stdio?

With stdio, each OpenCode session spawns its own server process. With `streamable-http`, one warm server handles all sessions. After starting the HTTP server, use the `type: "remote"` MCP config from section 2 above.

### 4. Optional: generate a `.lgrepignore`

```bash
lgrep init-ignore /path/to/project
```

### 5. Optional: inspect or prune orphan semantic caches

```bash
lgrep prune-orphans --dry-run
lgrep prune-orphans --execute --cache-dir /path/to/cache
```

`prune-orphans` is dry-run by default. Use `--execute` to actually delete orphaned semantic cache directories. `--cache-dir` overrides `LGREP_CACHE_DIR` for a single run. `--execute` and `--dry-run` are mutually exclusive; passing both exits with an error. Agents can call the same workflow via the `lgrep_prune_orphans` MCP tool listed in [Symbol tools](#symbol-tools); that path also skips projects currently loaded in the running server.

**Grace window.** Recently modified cache dirs are preserved for 1 hour by default so the pruner cannot race a live indexer. Override with `LGREP_PRUNE_MIN_AGE_S=` (`0` disables grace entirely). The `missing_meta` and `project_path_enoent` reasons bypass the grace check because they are unambiguous.

**Transport-aware MCP safety.** When lgrep is reached over a shared transport (for example `streamable-http`), the MCP tool coerces `dry_run=True` regardless of the caller's request. Destructive prunes on shared deployments must go through the CLI (`lgrep prune-orphans --execute`) so the operator is explicit.

### Troubleshooting `prune-orphans --execute`

Each orphan is deleted independently. If `shutil.rmtree` fails for one entry (for example a lingering file lock or permission issue), the batch continues and the failure is recorded in the response under `failures[]` as `{path, error}`; the rest of the reclaim still lands. Re-run `lgrep prune-orphans --execute` after addressing the error, or inspect with `--dry-run` first to confirm the orphan is still present.

Deletion is refused for any path outside the resolved cache directory (path-confinement guard) and for any symlinked cache entry (TOCTOU guard) — both show up in `failures[]` rather than as successful deletes.

### 6. Optional: inspect or prune stale symbol-store indexes

```bash
lgrep prune-symbols --dry-run
lgrep prune-symbols --execute --storage-dir /path/to/storage
```

`prune-symbols` is dry-run by default. Use `--execute` to actually delete stale symbol-store index files (`index_.json`). `--storage-dir` overrides `LGREP_SYMBOLS_DIR` for a single run (default: `~/.cache/lgrep/symbols/`). `--execute` and `--dry-run` are mutually exclusive; passing both exits with an error. Agents can call the same workflow via the `lgrep_prune_symbols` MCP tool listed in [Symbol tools](#symbol-tools); that path also skips projects currently loaded in the running server.

**Grace window.** Recently modified index files are preserved for 1 hour by default so the pruner cannot race a live indexer. Override with `LGREP_PRUNE_MIN_AGE_S=` (`0` disables grace entirely). Only the `unreadable_index_json` reason is grace-eligible; the `repo_path_enoent` and `missing_repo_path_field` reasons bypass the grace check because they are unambiguous.

**Transport-aware MCP safety.** When lgrep is reached over a shared transport (for example `streamable-http`), the MCP tool coerces `dry_run=True` regardless of the caller's request. Destructive symbol-store prunes on shared deployments must go through the CLI (`lgrep prune-symbols --execute`) so the operator is explicit.

### Troubleshooting `prune-symbols --execute`

Each stale index is deleted independently. If an unlink fails for one entry (for example a lingering file lock or permission issue), the batch continues and the failure is recorded in the response under `failures[]` as `{path, error}`; the rest of the reclaim still lands. Re-run `lgrep prune-symbols --execute` after addressing the error, or inspect with `--dry-run` first to confirm the stale index is still present.

Deletion is refused for any path outside the resolved storage directory (path-confinement guard) and for any symlinked index file (TOCTOU guard) — both show up in `failures[]` rather than as successful deletes.

## First-use workflow

Typical OpenCode flow:

1. Ask an intent question with `lgrep_search_semantic`
2. Inspect structure with `lgrep_get_file_outline` or `lgrep_get_repo_outline`
3. Retrieve exact symbols with `lgrep_search_symbols` and `lgrep_get_symbol`

Examples:

```text
lgrep_search_semantic(query="authentication flow", path="/path/to/project")
lgrep_get_file_outline(path="/path/to/project/src/auth.py")
lgrep_index_symbols_folder(path="/path/to/project")
lgrep_search_symbols(query="authenticate", path="/path/to/project")
lgrep_get_symbol(symbol_id="src/auth.py:function:authenticate", path="/path/to/project")
```

High-value prompts:

- "Where do we enforce auth between route and service?"
- "Find the `authenticate` function"
- "What are the main symbols in `src/auth.py`?"
- "Show me the repo structure around billing"
- "Find references to `verifyToken`"

## Tool selection guide

| Task | Best tool | Why |
|---|---|---|
| Intent or concept discovery | `lgrep_search_semantic` | Search by meaning |
| Find a function or class by name | `lgrep_search_symbols` | Exact symbol lookup |
| Inspect a single file's structure | `lgrep_get_file_outline` | Fast AST outline |
| Inspect repo structure | `lgrep_get_repo_outline` | Symbol-level overview |
| Find exact text or identifiers | `lgrep_search_text` or `grep` | Literal match |
| Retrieve exact source for a symbol | `lgrep_get_symbol` | Targeted code retrieval |
| Read a known file directly | `Read` | No search needed |

## MCP response format

As of `3.0.0`, every lgrep MCP tool returns a structured dict matching a declared TypedDict in [`src/lgrep/server/responses.py`](src/lgrep/server/responses.py). Clients should consume responses as native dicts — no `json.loads` is needed.

Example — `lgrep_search_semantic`:

```python
{
    "query": "authentication flow",
    "path": "/path/to/project",
    "engine": "hybrid",
    "total": 3,
    "results": [
        {"file_path": "src/auth.py", "line_number": 42,
         "content": "...", "score": 0.91,
         "start_line": 42, "end_line": 87, "match_type": "hybrid"},
        # ...
    ],
}
```

`engine` is `"hybrid"` when `hybrid=true` (the default) or `"vector"` when `hybrid=false`.

Error responses use the shared `ToolError` shape:

```python
{"error": "VOYAGE_API_KEY not set. Cannot perform semantic search."}
```

Before `3.0.0`, tools returned these objects as `json.dumps(...)` strings. If you upgrade from `2.x`, remove any `json.loads(response)` wrappers on tool output. See the [Upgrade from 2.x](CHANGELOG.md#upgrade-from-2x) notes in the changelog for the full migration path.

## MCP tools

### Semantic tools

| Tool | Purpose |
|---|---|
| `lgrep_search_semantic(query, path, limit=10, hybrid=true)` | Search code by meaning |
| `lgrep_index_semantic(path)` | Build or refresh a semantic index |
| `lgrep_status_semantic(path?)` | Show semantic index and watcher status |
| `lgrep_watch_start_semantic(path)` | Start background semantic re-indexing |
| `lgrep_watch_stop_semantic(path?)` | Stop the watcher |

### Symbol tools

| Tool | Purpose |
|---|---|
| `lgrep_index_symbols_folder(path, max_files=500, incremental=True)` | Index symbols in a local folder |
| `lgrep_index_symbols_repo(repo, ref="HEAD")` | Index symbols from a GitHub repo |
| `lgrep_list_repos()` | List indexed symbol repos |
| `lgrep_get_file_tree(path, max_files=500)` | Show repo file tree |
| `lgrep_get_file_outline(path)` | Show symbol outline for one file |
| `lgrep_get_repo_outline(path, max_files=500)` | Show symbol outline for a repo |
| `lgrep_search_symbols(query, path, limit=20, kind?)` | Search symbols by name |
| `lgrep_search_text(query, path, max_results=50)` | Search literal text |
| `lgrep_get_symbol(symbol_id, path)` | Retrieve one symbol |
| `lgrep_get_symbols(symbol_ids, path)` | Retrieve multiple symbols |
| `lgrep_invalidate_cache(path)` | Drop the symbol index for a repo |
| `lgrep_prune_orphans(dry_run=True)` | Report (or with `dry_run=False`, delete) orphan semantic cache dirs; skips active projects and the `symbols/` cache |
| `lgrep_prune_symbols(dry_run=True)` | Report (or with `dry_run=False`, delete) stale symbol-store index files; skips active projects and non-local `github:` entries |

### Symbol ID format

Symbol IDs use this deterministic format:

```text
file_path:kind:name
```

Examples:

```text
src/

…

## Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [Sharper-Flow](https://github.com/Sharper-Flow)
- **Source:** [Sharper-Flow/lgrep](https://github.com/Sharper-Flow/lgrep)
- **License:** MIT
- **Homepage:** https://sharperflow.com/projects/lgrep

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** yes
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/mcp-sharper-flow-lgrep
- Seller: https://agentstack.voostack.com/s/sharper-flow
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
