locus-mcp
v0.2.0
Published
Locus MCP - AI-driven semantic code search client
Maintainers
Readme
Locus MCP
Forked from the upstream
locus-mcpproject. This build adds a hosted mode: clients talk to the self-hosted Locus service instead of connecting directly to Windsurf. The service injects the real Windsurf authentication server-side, so end users never need a local Windsurf installation or API key.
Hosted Mode (Locus)
Set these two environment variables to enable hosted mode:
| Variable | Example | Purpose |
| --- | --- | --- |
| LOCUS_BASE_URL | https://ace.panrun.me/relay | Base URL of the Locus service. When set, requests go to ${LOCUS_BASE_URL}/exa.api_server_pb.ApiServerService/... and ${LOCUS_BASE_URL}/exa.auth_pb.AuthService/... instead of Windsurf. |
| LOCUS_TOKEN | ace_xxxxxxxx | Your Locus key. Sent as Authorization: Bearer ${LOCUS_TOKEN} on every request. |
Example MCP config (hosted mode, via npm):
{
"mcpServers": {
"locus": {
"command": "npx",
"args": ["-y", "locus-mcp"],
"env": {
"LOCUS_BASE_URL": "https://ace.panrun.me/relay",
"LOCUS_TOKEN": "your-locus-api-key"
}
}
}
}In hosted mode the client does not read your local Windsurf install or WINDSURF_API_KEY; a placeholder key is encoded into the protobuf body and the service replaces it with the real Windsurf credentials. Local command execution (rg / readfile / tree / ls / glob) is unchanged.
Legacy / direct mode: if LOCUS_BASE_URL is not set, the client behaves exactly like upstream and connects directly to Windsurf (see the original docs below).
Any MCP-compatible client (Claude Code, Claude Desktop, Cursor, etc.) can use this to search codebases with natural language queries. All tools are bundled via npm — no system-level dependencies needed (ripgrep via @vscode/ripgrep, tree via tree-node-cli). Works on macOS, Windows, and Linux.
How It Works
You: "where is the authentication logic?"
│
▼
┌─────────────────────────┐
│ Locus MCP │
│ (local MCP server) │
│ │
│ 1. Maps project → /codebase
│ 2. Sends query to Windsurf Devstral API
│ 3. AI generates rg/readfile/tree commands
│ 4. Executes commands locally (built-in rg)
│ 5. Returns results to AI
│ 6. Repeats for N rounds
│ 7. Returns file paths + line ranges
│ + suggested search keywords
└─────────────────────────┘
│
▼
Found 3 relevant files.
[1/3] /project/src/auth/handler.py (L10-60)
[2/3] /project/src/middleware/jwt.py (L1-40)
[3/3] /project/src/models/user.py (L20-80)
Suggested search keywords:
authenticate, jwt.*verify, session.*tokenPrerequisites
- Node.js >= 18
No need to install ripgrep — it's bundled via @vscode/ripgrep.
Installation
Option 1: npm (Recommended)
npm install locus-mcp
# or run directly
npx -y locus-mcpOption 2: From Source
git clone https://github.com/zj0808/locus-mcp.git
cd locus-mcp
npm installSetup (Hosted Mode, Recommended)
Get a key from the Locus console, then add to your editor's MCP config (Claude Code ~/.claude.json, Claude Desktop claude_desktop_config.json, Cursor mcp.json, etc.):
{
"mcpServers": {
"locus": {
"command": "npx",
"args": ["-y", "locus-mcp"],
"env": {
"LOCUS_BASE_URL": "https://ace.panrun.me/relay",
"LOCUS_TOKEN": "your-locus-api-key"
}
}
}
}Setup (Legacy Direct Mode)
Without LOCUS_BASE_URL, the client connects directly to Windsurf and needs a Windsurf account (free tier works). The server auto-extracts the API key from your local Windsurf installation; you can also use the extract_windsurf_key MCP tool after setup, or set WINDSURF_API_KEY manually.
{
"mcpServers": {
"locus": {
"command": "npx",
"args": ["-y", "locus-mcp"],
"env": {
"WINDSURF_API_KEY": "sk-ws-01-xxxxx"
}
}
}
}If
WINDSURF_API_KEYis omitted, the server auto-discovers it from your local Windsurf installation.
Environment Variables
| Variable | Default | Description |
|----------|---------|-------------|
| WINDSURF_API_KEY | (auto-discover) | Windsurf API key |
| LOCUS_MAX_TURNS | 3 | Search rounds per query (more = deeper but slower) |
| LOCUS_MAX_COMMANDS | 8 | Max parallel commands per round |
| LOCUS_TIMEOUT_MS | 30000 | Connect-Timeout-Ms for streaming requests |
| LOCUS_GRAPH_HINTS | auto | Local JS/TS symbol graph hints: auto (index exists, or auto-build on JS/TS projects), true, false |
| LOCUS_GRAPH_AUTO_BUILD | true | When graph is enabled, auto-sync/build index if missing or stale |
| LOCUS_ROUTE | auto | Query routing: auto classifies intent (symbol/semantic/exact/impact/complex), coordinates the built-in Graph and Hybrid indexes, and adapts max_turns. off disables routing. See docs/route-policy.md |
| LOCUS_LOCAL_FIRST | auto | Skip remote verification only when built-in retrieval confidence is high; on is more aggressive and off always verifies remotely |
| LOCUS_SPEED_PROFILE | balanced | quality, balanced, or fast; controls local-first confidence thresholds |
| LOCUS_DEFAULT_EXCLUDES | (empty) | Extra comma-separated exclude patterns merged into defaults |
| LOCUS_REPO_MAP_CACHE_MS | 45000 | Cache repo tree map TTL in ms (0 disables) |
| LOCUS_HIDE_EXTRACT_WINDSURF_KEY_TOOL | false | Hide extract_windsurf_key from MCP tools when set to 1, true, yes, or on |
| LOCUS_RESULT_MAX_LINES | 50 | Max lines per command output (truncation) |
| LOCUS_LINE_MAX_CHARS | 250 | Max characters per output line (truncation) |
| WS_MODEL | MODEL_SWE_1_6_FAST | Windsurf model name |
| WS_APP_VER | 1.48.2 | Windsurf app version (protocol metadata) |
| WS_LS_VER | 1.9544.35 | Windsurf language server version (protocol metadata) |
Available Models
The model can be changed by setting WS_MODEL (see environment variables above).

Default: MODEL_SWE_1_6_FAST — fastest speed, richest grep keywords, finest location granularity.
MCP Tools
locus_mcp_search
AI-driven semantic code search with tunable parameters.
| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| query | string | Yes | — | Natural language search query |
| project_path | string | No | cwd | Absolute path to project root |
| tree_depth | integer | No | 3 | Directory tree depth for repo map (1-6). Higher = more context but larger payload. Auto falls back to lower depth if tree exceeds 250KB. Use 1-2 for huge monorepos (>5000 files), 3 for most projects, 4-6 for small projects. |
| max_turns | integer | No | 3 | Search rounds (1-5). More = deeper search but slower. Use 1-2 for simple lookups, 3 for most queries, 4-5 for complex analysis. |
| max_results | integer | No | 10 | Upper bound for files to return (1-30). Locus dynamically selects the useful TopK instead of padding weak results. |
Returns:
- Relevant files with line ranges
- Suggested search keywords (rg patterns used during AI search)
- Diagnostic metadata (
[config]line showing actual tree_depth used, tree size, and whether fallback occurred)
Example output:
Found 3 relevant files.
[1/3] /project/src/auth/handler.py (L10-60, L120-180)
[2/3] /project/src/middleware/jwt.py (L1-40)
[3/3] /project/src/models/user.py (L20-80)
grep keywords: authenticate, jwt.*verify, session.*token
[config] tree_depth=3, tree_size=12.5KB, max_turns=3Error output includes status-specific hints:
Error: Request failed: HTTP 403
[hint] 403 Forbidden: Authentication failed. The API key may be expired or revoked.
Try re-extracting with extract_windsurf_key, or set a fresh WINDSURF_API_KEY env var.
If you are running inside WSL, run `devin login` inside WSL so `~/.local/share/devin/credentials.toml` exists.Error: Request failed: HTTP 413
[diagnostic] tree_depth_used=3, tree_size=280.0KB (auto fell back from requested depth)
[hint] If the error is payload-related, try a lower tree_depth value.extract_windsurf_key
Extract Windsurf API Key from local installation. No parameters.
Set LOCUS_HIDE_EXTRACT_WINDSURF_KEY_TOOL=1 at MCP server startup to hide this tool from tools/list. This does not disable internal API-key auto-discovery for locus_mcp_search.
locus_index_status
Reports the native Graph and Hybrid index state for a project or package root:
- freshness, version, file count, chunk count, and structured-chunk count
- loaded
.gitignore,.cursorignore,.locusignore,.ignore, and.dockerignoresources - watcher state, scan fallback, and incremental changed/reused/deleted counts
Set refresh=true to synchronize both indexes before returning status.
Project Structure
locus-mcp/
├── package.json
├── src/
│ ├── server.mjs # MCP server entry point
│ ├── core.mjs # Auth, message building, streaming, search loop
│ ├── executor.mjs # Tool executor: rg, readfile, tree, ls, glob
│ ├── extract-key.mjs # Windsurf API Key extraction (SQLite)
│ └── protobuf.mjs # Protobuf encoder/decoder + Connect-RPC frames
├── README.md
└── LICENSEHow the Search Works
- Project directory is mapped to virtual
/codebasepath - Directory tree generated at requested depth (default L=3), with automatic fallback to lower depth if tree exceeds 250KB
- Optional local JS/TS graph hints seed candidate files/symbols (verify with tools)
- Query + directory tree (+ hints) sent to Windsurf's Devstral model via Connect-RPC/Protobuf
- Devstral generates tool commands (ripgrep, file reads, tree, ls, glob)
- Commands executed locally in parallel (up to
LOCUS_MAX_COMMANDSper round) - Results sent back to Devstral for the next round
- After
max_turnsrounds, Devstral returns file paths + line ranges - All rg patterns used during search are collected as suggested keywords
- Diagnostic metadata and monorepo
[scope]hints appended for the calling AI
Search workflow (JetBrains-adapted)
For agent callers, follow one broad semantic bootstrap → local read → one path_filter narrowed retry. Do not re-broaden at monorepo root. Full comparison of JetBrains Context vs Locus: docs/jetbrains-context-comparison.md. Workflow: docs/search-workflow.md.
New tool param: path_filter — relative path under project_path (same idea as JetBrains pathFilter) for the narrowed retry.
Precision Tips (monorepo + graph)
# Build / refresh local JS/TS symbol index for a package or whole repo
npm run graph:index -- --project ./packages/api
# or from installed package
npx locus-mcp # graph is used automatically when .locus-mcp/graph exists or LOCUS_GRAPH_AUTO_BUILD=true- Narrow
project_path: preferpackages/foo/apps/webover monorepo root. - Graph:
LOCUS_GRAPH_HINTS=auto(default) +LOCUS_GRAPH_AUTO_BUILD=truekeeps the index fresh on JS/TS projects. - Turns: simple lookup
max_turns=1-2; cross-package tracing3-5.
Technical Details
- Protocol: Connect-RPC over HTTP/1.1, Protobuf encoding, gzip compression
- Model: Devstral (
MODEL_SWE_1_6_FAST, configurable) - Local tools:
rg(bundled via @vscode/ripgrep),readfile(Node.js fs),tree(tree-node-cli),ls(Node.js fs),glob(Node.js fs) - Auth: API Key → JWT (auto-fetched per session)
- Runtime: Node.js >= 18 (ESM)
Dependencies
| Package | Purpose |
|---------|---------|
| @modelcontextprotocol/sdk | MCP server framework |
| @vscode/ripgrep | Bundled ripgrep binary (cross-platform) |
| tree-node-cli | Cross-platform directory tree (replaces system tree) |
| better-sqlite3 | Read Windsurf's local SQLite DB |
| zod | Schema validation (MCP SDK requirement) |
License
MIT
Retrieval performance (monorepo)
Optimizations in this fork for better + faster search:
- Route auto-turns — symbol/semantic queries use fewer remote turns when the built-in indexes are confident
- Local radar adapt — Graph and native Hybrid confidence further clamps
max_turns - Parallel prep — JWT, Graph, Hybrid, and repo-map preparation overlap
- Caches — repo maps are cached and the native Hybrid index is incrementally persisted under
.locus-mcp/index - Lean excludes —
.locus-mcp,.codegraph, binaries/logs skipped from tree - Local-first - LOCUS_LOCAL_FIRST=auto skips remote Devstral when local radars are high-confidence
- Prebuild graph —
npm run graph:index -- --project <root>
The native Hybrid index is bundled with Locus and requires no external MCP server,
CLI or model download. See
docs/native-hybrid-retrieval.md.
Result quality now includes implementation-owner ranking, query-aware file roles, dynamic TopK selection, an approximately 20 percent related-result quota, Graph evidence chains, structural chunks, project ignore rules, and watch-driven incremental refresh with a scan fallback.
Run retrieval policy coverage and the quality dashboard with:
npm run smoke:retrieval
npm run benchmark:retrieval
# optional machine-readable report
node scripts/benchmark-retrieval-quality.mjs --json=./retrieval-quality.jsonBiggest accuracy lever remains: narrow project_path / path_filter (see docs/search-workflow.md).
