pitlane-mcp
Health Gecti
- License — License: Apache-2.0
- Description — Repository has a description
- Active repo — Last push 0 days ago
- Community trust — 49 GitHub stars
Code Gecti
- Code scan — Scanned 5 files during light audit, no dangerous patterns found
Permissions Gecti
- Permissions — No dangerous permissions requested
This MCP server provides token-efficient code navigation for AI coding agents. It uses AST parsing via tree-sitter to index a local codebase, allowing agents to retrieve specific code symbols rather than reading entire files into context.
Security Assessment
The overall risk is Low. The tool operates fully locally and explicitly states that it makes no network calls and does not connect to external APIs. A code scan of five files found no dangerous patterns, hardcoded secrets, or risky shell command executions. Furthermore, the server requests no dangerous system permissions.
Quality Assessment
The project is in very active development, with its most recent push occurring today. It has a solid and growing foundation of community trust, evidenced by 49 GitHub stars. The codebase is properly licensed under the permissive Apache-2.0 license. It includes continuous integration (CI) testing, and the repository is well-documented with a clear description and realistic feature set.
Verdict
Safe to use.
Token-efficient navigation substrate for AI coding agents. Index code once and retrieve only the symbols you need.
pitlane-mcp
Token-efficient code intelligence MCP server. Indexes a codebase once using tree-sitter AST parsing and lets AI agents retrieve exactly the symbols they need — instead of dumping entire files into context.
Why
AI coding agents default to reading whole files. With pitlane-mcp, they fetch only the symbol they need — 532× less tokens on a Rust codebase (ripgrep), 418× on C++ (LevelDB), 133× on C (Redis), 125× on Go (Gin), 112× on Java (Guava), 65× on C# (Newtonsoft.Json), 61× on Ruby (RuboCop), 56× on Kotlin (OkHttp), 54× on Objective-C (SDWebImage), 53× on TypeScript (Hono), 52× on Swift (SwiftLint), 49× on Solidity (OpenZeppelin Contracts), 41× on Svelte (svelte.dev), 20× on Python (FastAPI), 80× on PHP (Laravel), 801× on Zig (zls), 90× on Lua (Roact), and Bash (bats-core)¹.
Features
- AST-based indexing — tree-sitter parses Rust, Python, JavaScript, TypeScript, Svelte (embedded
<script>/<script lang="ts">blocks only), C, C++, Go, Java, C#, Ruby, Swift, Objective-C, PHP, Zig, Kotlin, Lua, Solidity, and Bash source into structured symbols - BM25 full-text search — tantivy-backed ranked search over name, qualified name, signature, and doc fields with a custom camelCase tokenizer (
LowerInstruction→["lower", "instruction"]); falls back to exact substring match if the index isn't ready - Graph-aware navigation tools — direct callers and callees for shallow impact checks without whole-repo back-and-forth
- Thirteen MCP tools for navigation: outline, search, fetch, line-range fetch, callers, callees, usages, index stats, usage stats
- Incremental re-indexing — background watcher re-parses only changed files
- Disk-persisted index — binary format, loads in milliseconds on subsequent calls
- Smart exclusions — automatically skips
.venv,node_modules,target,__pycache__,dist,.next, and other dependency/build trees at any depth - Fully local — no network calls, no external APIs
Supported Languages
| Language | Extensions | Symbol kinds |
|---|---|---|
| Rust | .rs |
function, method, struct, enum, trait, impl, mod, macro, const, type alias |
| Python | .py |
function, method, class |
| JavaScript | .js, .jsx, .mjs, .cjs |
function, class, method |
| TypeScript | .ts, .tsx, .mts, .cts |
function, class, method, interface, type alias, enum |
| Svelte | .svelte |
function, class, method, interface, type alias, enum (from embedded <script> / <script lang=\"ts\"> blocks only; template/style sections are not indexed) |
| C | .c, .h |
function, struct, enum, type alias, macro |
| C++ | .cpp, .cc, .cxx, .hpp, .hxx |
function, method, class, struct, enum, type alias, macro |
| Go | .go |
function, method, struct, interface, type alias |
| Java | .java |
class, interface, enum, method |
| C# | .cs |
class, struct, interface, enum, method, type alias |
| Bash | .sh, .bash |
function |
| Ruby | .rb |
class, module, method |
| Swift | .swift |
class, struct, enum, protocol, method, function, init, type alias |
| Objective-C | .m, .mm |
class, protocol, method, function, type alias |
| PHP | .php |
class, interface, enum, method, function |
| Zig | .zig |
function, method, struct, enum, const |
| Lua | .luau, .lua |
function, method, type alias |
| Kotlin | .kt, .kts |
class, interface, enum, object, function, method, type alias |
| Solidity | .sol |
contract, interface, library, function, method, modifier, constructor, event, error, struct, enum |
TypeScript declaration files (.d.ts, .d.mts, .d.cts) are automatically skipped.
Installation
Download a pre-built binary from GitHub Releases for Linux (x86_64, aarch64), macOS (x86_64, Apple Silicon), and Windows (x86_64).
Or install via Homebrew (macOS):
brew tap eresende/pitlane-mcp
brew install pitlane-mcp
Or install via cargo-binstall (pulls pre-built binaries, no compilation needed):
cargo binstall pitlane-mcp
Or install from crates.io (requires Rust 1.75+):
cargo install pitlane-mcp
Or build from source:
cargo build --release
cp target/release/pitlane-mcp ~/.local/bin/
cp target/release/pitlane ~/.local/bin/
MCP Client Configuration
Claude Code
claude mcp add pitlane-mcp -- pitlane-mcp
OpenCode
Add to your opencode.json or opencode.jsonc:
{
"mcp": {
"pitlane-mcp": {
"type": "local",
"command": ["pitlane-mcp"]
}
}
}
VS Code / Kiro IDE
Add to your MCP settings (.kiro/settings/mcp.json or .vscode/mcp.json):
{
"mcp": {
"servers": {
"pitlane-mcp": {
"type": "stdio",
"command": "pitlane-mcp",
"args": []
}
}
}
}
Tools
index_project
Parse and index all supported source files under a path.
{ "path": "/your/project", "force": false }
Returns symbol count, file count, index path, and elapsed time. Subsequent calls return cached results unless force: true.
Optional parameters:
exclude— additional glob patterns to skip during the walk (e.g."vendor/**").force: true— rebuild the index even if the on-disk copy is up to date.max_files— cap on the number of source files indexed (default: 100 000). Raise this for very large mono-repos. If the walk finds more eligible files than the cap,index_projectreturns aFILE_LIMIT_EXCEEDEDerror instead of indexing.
search_symbols
Search by name, kind, language, or file pattern.
{ "project": "/your/project", "query": "authenticate", "kind": "method" }
Defaults to BM25 ranked full-text search (via tantivy) over name, qualified name, signature, and doc fields — results are ordered by relevance. Pass "mode": "exact" for substring matching or "mode": "fuzzy" for trigram similarity. If the BM25 index isn't ready yet (e.g. first call after an upgrade), it falls back to exact automatically.
get_symbol
Retrieve the source of one symbol by its stable ID. Much cheaper than reading the whole file.
{ "project": "/your/project", "symbol_id": "src/auth.rs::Auth::login#method" }
Optional parameters:
signature_only— returns only the indexed metadata (signature, doc comment, file, line range) with no file I/O. Defaults totruefor struct, class, interface, and trait kinds; defaults tofalsefor functions, methods, and everything else. Passsignature_only: falseexplicitly to get the full body of a container type.include_context: true— includes 3 lines of surrounding source before and after the symbol.
Full-source responses include a references field — a list of symbols whose names appear as identifiers in the source. This saves a separate find_usages call when you want to understand what a symbol depends on.
Python/JS/TS/Java classes, C++ classes/structs, C# classes/structs, Ruby classes/modules, and Swift classes/structs: for classes that contain methods,
get_symbolreturns only the class header (plus docstring for Python) — not the full body. Objective-C@interfaceblocks are returned at full extent (they contain only declarations, not implementations). Retrieve individual methods by their own symbol IDs (e.g.models.py::MyClass::some_method#method). Useget_file_outlineto list all methods first.
get_file_outline
List all symbols in a file with kinds and line numbers — no source returned.
{ "project": "/your/project", "file_path": "src/auth.rs" }
get_project_outline
High-level overview of the project: files grouped by directory with symbol counts per kind.
{ "project": "/your/project", "depth": 2 }
Optional parameters:
summary: true— return only directory names with file and symbol counts, no per-file items or kind breakdowns. Use for very large codebases (>10k files) where the full outline exceeds token limits. Agents should retry with this flag if the normal response is too large.path— scope the outline to a subtree (e.g."kernel/sched"). Combine withdepthto drill into a specific area of a large repo.max_dirs— cap the number of directory entries returned (default: 50, max: 500). When the result is truncated, the response includes ahintsuggestingpathorsummary: true.
find_usages
Find all locations that reference a symbol by name.
{ "project": "/your/project", "symbol_id": "src/auth.rs::Auth::login#method" }
AST-based reference search — only true identifier nodes are matched. String literals, comments, and substrings of longer identifiers are never returned.
Svelte note: reference search only covers identifiers inside embedded
<script>/<script lang="ts">blocks. Template and style sections are intentionally ignored.
find_callees
Return direct outgoing references for one symbol — useful for seeing what a function or method likely calls before reading more code.
{ "project": "/your/project", "symbol_id": "src/auth.rs::Auth::login#method" }
Optional parameters:
limit— maximum callees to return (default: 100).offset— offset into callees for pagination.
This is intentionally shallow and lightweight. Results are heuristic direct references, not a full semantic call graph.
find_callers
Return direct incoming references for one symbol — useful for quick impact checks before changing a function or method.
{ "project": "/your/project", "symbol_id": "src/auth.rs::Auth::login#method" }
Optional parameters:
scope— restrict callers to a file or directory glob.limit— maximum callers to return (default: 100).offset— offset into callers for pagination.
Like find_callees, this stays shallow by design and returns heuristic direct callers, not a full transitive call graph.
get_lines
Fetch a slice of a file by line range — useful for blocks that are not named symbols (macro invocation tables, initializer arrays, inline comment blocks, etc.).
{ "project": "/your/project", "file_path": "fs/read_write.c", "line_start": 668, "line_end": 700 }
Returns source, total_file_lines, and the actual line_end after clamping. Capped at 500 lines per call; when the cap is hit the response includes truncated: true and a truncated_note with the next line_start to continue.
get_index_stats
Return symbol counts by language and kind for an indexed project — lightweight orientation tool. Use instead of get_project_outline when you only need aggregate numbers, not the file tree.
{ "project": "/your/project" }
Returns total_files, total_symbols, by_language, and by_kind, all sorted by count descending.
get_usage_stats
Return token-efficiency statistics for get_symbol calls — how many tokens were saved by signature-only responses, persisted across sessions.
{ "project": "/your/project" }
Returns global totals and a per-project breakdown with get_symbol_calls, signature_only_applied, full_source_bytes, returned_bytes, and tokens_saved_approx. Stats are stored at ~/.pitlane/stats.json.
watch_project
Start incremental background re-indexing on file changes.
{ "project": "/your/project" }
{ "project": "/your/project", "stop": true }
{ "project": "/your/project", "status_only": true }
Pass status_only: true to check whether a watcher is already running without starting or stopping it — returns "status": "watching" or "status": "not_watching".
CLI
The pitlane binary exposes the same code intelligence as the MCP server, useful for shell scripts, CI pipelines, or manual exploration.
pitlane index
Index a project (or load from cache if up to date).
pitlane index /your/project
pitlane index /your/project --force
pitlane index /your/project --exclude "*.generated.ts" --exclude "vendor/**"
pitlane search
Search for symbols by name with optional filters.
pitlane search /your/project authenticate
pitlane search /your/project authenticate --kind method
pitlane search /your/project authenticate --lang python
pitlane search /your/project authenticate --file "src/auth*"
pitlane search /your/project authenticate --limit 5 --offset 10
pitlane outline
High-level directory/symbol overview of the project.
pitlane outline /your/project
pitlane outline /your/project --depth 3
pitlane file
List all symbols in a file with kinds and line numbers.
pitlane file /your/project src/auth.rs
pitlane symbol
Fetch the source of a single symbol by its ID.
pitlane symbol /your/project src/auth.rs::Auth::login[method]
pitlane symbol /your/project src/auth.rs::Auth::login[method] --context
pitlane symbol /your/project src/auth.rs::Auth::login[method] --sig-only
pitlane callees
Show direct outgoing references for a symbol.
pitlane callees /your/project src/auth.rs::Auth::login[method]
pitlane callees /your/project src/auth.rs::Auth::login[method] --limit 20 --offset 20
pitlane callers
Show direct incoming references for a symbol.
pitlane callers /your/project src/auth.rs::Auth::login[method]
pitlane callers /your/project src/auth.rs::Auth::login[method] --scope "src/**" --limit 20
All commands output JSON to stdout. Logs go to stderr and can be controlled with RUST_LOG.
Symbol IDs
Symbol IDs are stable string keys derived from the file path, qualified name, and kind:
{relative_path}::{qualified_name}#{kind}
src/audio/engine.rs::Engine::process_block#method
src/models/user.py::UserService::authenticate#method
src/api/client.ts::fetchUser#function
src/components/Button.tsx::Button#function
IDs are returned by search_symbols and get_file_outline and used as input to get_symbol, find_callees, find_callers, and find_usages.
Index Storage
Indexes are stored at:
~/.pitlane/indexes/{project_hash}/index.bin
~/.pitlane/indexes/{project_hash}/meta.json
The project hash is a BLAKE3 hash of the canonical project path. The index is invalidated automatically when source files change (mtime-based). Use force: true to rebuild unconditionally.
Recommended Agent Instructions
Add a CLAUDE.md at your project root to guide the agent:
# Code Navigation
Use pitlane-mcp for all code lookups when available.
1. Call index_project at the start of each session to load the index.
2. Call watch_project right after indexing to keep the index up to date as files change.
3. Before reading any file, call get_file_outline to see its structure without consuming its full content.
4. Use search_symbols to find functions/types by name. If no exact match is found it falls back to fuzzy matching automatically.
5. Use get_symbol to retrieve only the exact implementation you need, not the whole file.
6. Use find_callees to see what a symbol directly depends on before opening more files.
7. Use find_callers before changing a symbol to get a shallow impact view.
8. Use find_usages before refactoring any public symbol or when you need exhaustive name-based matches.
9. For struct/class/interface/trait symbols, get_symbol returns signature-only by default. Pass signature_only=false to get the full body and the references list.
10. Use get_lines to fetch a specific block by line range when it isn't a named symbol.
11. Use get_index_stats to orient yourself in a new codebase without burning tokens on get_project_outline.
12. Fall back to direct file reads only when editing or when full file context is genuinely required.
Benchmarks
Each language is benchmarked against a pinned open-source project chosen for real-world representativeness. New corpora are added as language support grows.
Note: pitlane-mcp is under active development. New language support and token-efficiency optimizations land frequently, so these numbers are updated with each release and may change significantly between versions.
Test environment: AMD Ryzen 9 9950X (16 cores / 32 threads), 32 GB RAM, NVMe SSD.
Results
| Corpus | Language | Files | Symbols | Index time¹ | Token eff.² | search_symbols |
get_symbol |
|---|---|---|---|---|---|---|---|
| ripgrep 14.1.1 | Rust | 101 | 3,207 | 248 ms | 532× | 55.8 µs | 17.5 µs |
| FastAPI 0.115.6 | Python | 1,290 | 4,828 | 256 ms | 20× | 54.8 µs | 17.5 µs |
| Hono v4.7.4 | TypeScript | 368 | 992 | 240 ms | 53× | 25.0 µs | 60.5 µs |
| svelte.dev @ 44823b4 | Svelte | 841 | 685 | 240 ms | 41× | 26.9 µs | 17.5 µs |
| Redis 7.4.2 | C | 818 | 14,648 | 344 ms | 133× | 35.7 µs | 17.5 µs |
| LevelDB 1.23 | C++ | 132 | 1,531 | 231 ms | 418× | 38.5 µs | 17.5 µs |
| Gin v1.10.0 | Go | 92 | 1,184 | 235 ms | 125× | 50.5 µs | 17.5 µs |
| Guava v33.4.8 | Java | 3,275 | 56,805 | 975 ms | 112× | 88.5 µs | 17.5 µs |
| Newtonsoft.Json 13.0.3 | C# | 933 | 7,284 | 312 ms | 65× | 22.2 µs | 17.5 µs |
| bats-core v1.11.1 | Bash | 54 | 147 | 222 ms | N/A³ | 21.3 µs | 30.7 µs |
| RuboCop v1.65.0 | Ruby | 1,539 | 9,122 | 290 ms | 61× | 56.1 µs | 17.5 µs |
| SwiftLint 0.57.0 | Swift | 667 | 3,781 | 248 ms | 52× | 36.6 µs | 17.5 µs |
| SDWebImage 5.19.0 | Objective-C | 271 | 1,564 | 237 ms | 54× | 20.8 µs | 17.5 µs |
| Laravel v11.9.2 | PHP | 2,331 | 26,127 | 612 ms | 80× | 66.9 µs | 17.6 µs |
| OpenZeppelin Contracts v5.6.1 | Solidity | 661 | 4,073 | 23.4 ms | 49× | 80.2 µs | 23.0 µs |
| zls 0.13.0 | Zig | 67 | 2,422 | 240 ms | 801× | 51.4 µs | 17.7 µs |
| OkHttp 5.3.2 | Kotlin | 636 | 6,680 | 278 ms | 56× | 52.3 µs | 17.9 µs |
| Roact v1.4.4 | Lua | 95 | 93 | 223 ms | 90× | 21.3 µs | 22.7 µs |
¹ Median of 5 runs. ² Token efficiency is the median ratio of full-file size to symbol size across all class/struct/interface/type-alias symbols — how many times cheaper get_symbol is versus reading the whole file. ³ Bash has no class/struct symbols, only functions, so the metric does not apply.
search_symbolslatencies use the default BM25 mode (tantivy ranked full-text). Measured with Criterion over 100 samples per corpus. BM25 query time remains largely independent of corpus size — 21–89 µs across all 18 repos — because tantivy's inverted index avoids a linear symbol scan. The exact substring path now ranges from faster than BM25 on the tiniest corpora to 61× slower on Guava, because deterministic pagination sorts the full match set before slicing. Fuzzy (trigram) ranges from 4× to 792× slower and remains an explicit opt-in.LevelDB's 418× median reflects C++ class body trimming — inline method bodies are stripped, leaving only the class header. FastAPI's 20× median is lower than most because Pydantic models are large by nature (
Schemaalone is 4.8 KB). svelte.dev's 41× median reflects meaningful symbol extraction from embedded<script>blocks across a large Svelte-heavy monorepo while still excluding template/style sections. Guava's 975 ms index time and 56,805 symbols make it the heaviest corpus by a factor of 4×;get_project_outlineagainst it takes ~13.4 ms vs. sub-1.8 ms for every corpus except Laravel. Laravel'sget_symbollatency of 17.6 µs reflects the benchmark target beingEnumerable— a 36 KB interface that is nearly the entire file it lives in; interface bodies are never trimmed since their signatures are the API contract. OpenZeppelin Contracts lands at a 48.8× median because large Solidity interfaces such asIAccessManagerare intentionally returned at full extent; unlike contracts with executable bodies, interface definitions are not trimmed. zls's 801× median reflects Zig's tendency toward large files with many small struct/enum declarations;src/lsp.zigalone is 347 KB and contains hundreds of compact LSP message types.
Running the benchmarks
Clone the test corpora first (one-time setup):
bash bench/setup.sh
Memory, disk, and token efficiency (single binary, human-readable output):
# All repos
cargo run --release --features memory-bench --bin memory_bench
# One or more repos by name
cargo run --release --features memory-bench --bin memory_bench -- bats
cargo run --release --features memory-bench --bin memory_bench -- ripgrep fastapi
Query latency (Criterion, saves baseline for regression tracking):
# All repos
cargo bench --bench queries
# One repo (Criterion's built-in filter)
cargo bench --bench queries -- bats
cargo bench --bench queries -- "ripgrep|gin"
Indexing throughput (Criterion):
cargo bench --bench indexing
Experimental Features
Semantic Search
pitlane-mcp supports opt-in semantic search powered by locally-running embedding models via Ollama or LM Studio. When enabled, search_symbols gains a "semantic" mode that ranks results by meaning rather than keyword overlap — useful for finding symbols by concept when you don't know their exact names.
See SEMANTIC_SEARCH.md for setup instructions, model recommendations, and known limitations.
Security
pitlane-mcp is a fully local tool with no network calls. The following design properties are intentional but worth understanding before deploying it with AI agents.
Filesystem access scope
index_project, find_usages, and watch_project accept any path accessible to the running process — there is no allowlist or project-root confinement. An AI agent (or a prompt-injected instruction) can call these tools with sensitive directories such as ~/.ssh, ~/.config, or /etc.
Mitigating factors:
- Only files with recognized source extensions are indexed or read (
.rs,.py,.js,.ts,.tsx,.c,.cpp,.h,.hpp,.go,.swift,.m,.mm,.php,.zig,.luau,.lua,.sol, etc.). Most sensitive files — SSH keys, certificates,.envfiles — have no matching extension and are silently skipped. - Symbolic links are never followed (
follow_links: falsein all directory walks). - Files larger than 1 MiB are skipped.
Recommendation: If you deploy pitlane-mcp with an AI agent in an environment where prompt injection is a concern, treat it as having read access to any source file the OS user can read.
Resource cap on directory walks
index_project enforces a configurable max_files cap (default: 100,000 source files). If the walk finds more eligible files than the cap, it returns a FILE_LIMIT_EXCEEDED error instead of indexing. This prevents accidental full-filesystem walks (e.g. index_project("/")). Raise max_files explicitly for very large mono-repos.
Index storage
Indexes are stored unencrypted at ~/.pitlane/indexes/{blake3_hash}/. If another local user or process can write to your home directory they could tamper with index files; however, deserialization failures are handled gracefully and will not execute arbitrary code.
License
Licensed under either of MIT License or Apache License, Version 2.0, at your option.
Yorumlar (0)
Yorum birakmak icin giris yap.
Yorum birakSonuc bulunamadi