lens-mcp
lens-mcp
Deterministic navigation maps over code AND markdown — for AI agents. One MCP server, two lenses: tree-sitter for source (TypeScript / JavaScript / Python) and a markdown lens for docs. It answers "where is X and what's the shape of this project?" in one cheap call — so an agent spends context on thinking, not on browsing files.
Speaks the Model Context Protocol; works with any MCP client — Claude Code, Cursor, Codex, or your own agent.
Why
An agent orienting in an unfamiliar repo otherwise burns tokens ls-ing,
grep-ing, and Reading whole files to find the one function or the right
doc. lens returns the map instead of the territory:
function_bodyreads one function — often ~99% less context than Reading the file it lives in.headingreads one doc section — the referenced heading and its subsections, nothing else.mapreturns the whole project's surface — every code file's structure and every doc's outline — in a single call.
Every output is deterministic (real parsing, not a model summarizing), capped
with an honest truncated flag, and framed by one contract:
lens is a navigation map. Use it to locate, then Read the real source/section before judging or modifying it. A signature is not the body; an outline is not the section.
Related MCP server: code-analyze-mcp
Tools (13)
Orientation
Tool | What it does |
| Whole-tree surface in one call: per code file → structure; per doc → title + outline. Both families, one response. |
| Version, sandbox root, supported languages/extensions, tool list, every output cap, and the lens contract. |
| Install status, self-update ( |
Code (tree-sitter — .ts .tsx .mts .cts .js .jsx .mjs .cjs .py) + Prisma (.prisma)
Tool | What it does |
| One file's imports, exports, classes (+ methods), top-level functions, with line ranges. |
| Every function incl. nested — signatures, params/types, |
| Verbatim source of ONE function — the focused read. |
| Comments + |
| Locate a definition by name — functions, classes, and now |
| The inverse: who uses a symbol — call sites, imports, type-refs — tree-sitter-precise, no grep false positives. |
Docs (markdown — .md .markdown .mdx)
Tool | What it does |
| Full heading hierarchy (the TOC) with line numbers. |
| Read ONE section by heading text / slug / line number. |
| Extract inline / image / wikilink / autolink / reference links. |
| Case-insensitive full-text search across docs (heading hits ranked first). |
overview/find/map also cover Prisma schemas (schema.prisma → models, enums, fields, relations). JSON config/i18n isn't structurally mapped — lens says so honestly and points you to grep.
Call a code tool on a .md (or a doc tool on a .ts) and it fails with a
helpful pointer to the right tool — no silent confusion.
Honest by construction
Never silent data loss — a file with syntax errors returns
hasErrors+parseErrors, still extracting what it can; unparseable files inmap/findappear with an inlineerror, never vanish.Caps everywhere — every list is bounded (see
info.limits) and every cap is reported with the true total. A context-saving tool with unbounded output is self-refuting.Path sandbox — only files under the server's working directory are readable; escaping symlinks are rejected.
inforeports the root.Errors name the fix —
{error, path, hint}, withisErrorset.
Install
One line clones, installs, and writes a ready-to-paste MCP config with this install's absolute path:
git clone https://github.com/segentic-lab/lens-mcp && cd lens-mcp && ./install.shNo system packages and no native build — tree-sitter runs as WebAssembly, so it
works anywhere Node 18+ runs (Linux, macOS, Windows via WSL/Git Bash). The
installer checks Node, runs npm ci, builds (tsc → dist/), self-tests the full
suite (143 tests), and generates mcp-config.json.
Register it with your client — the generated mcp-config.json looks like:
{
"mcpServers": {
"lens": { "command": "node", "args": ["/abs/path/lens-mcp/dist/index.js"] }
}
}Claude Code:
claude mcp add lens -- node /abs/path/lens-mcp/dist/index.js(or copymcp-config.jsoninto a project as.mcp.json)Cursor / Windsurf: merge
mcp-config.jsoninto~/.cursor/mcp.jsonCodex CLI: add
[mcp_servers.lens]with the same command/args to~/.codex/config.toml
lens reads files under its working directory — the project your client
launches it in. Point it at a project and call map("."). Nothing outside the
working directory is readable.
Update later, in place:
./update.sh # git pull --ff-only + reinstall + self-testPrefer to do it by hand? npm install && npm run build && npm test, then
run node dist/index.js.
Lineage
lens-mcp supersedes the earlier split servers codelens-mcp (code) and
docslens-mcp (docs) — same engines, one server, one pipeline. Sibling of
periscope-mcp (web-app QA);
built to the same standard: honest errors, caps + truncated flags everywhere,
docs == behavior, tests before release.
Built by Segentic Lab. AGPL-3.0.
Available Tools
13 toolscommentsA
All comments in a source file with 1-based line ranges, kind (line | block | doc — doc covers /** */ and Python docstrings), and marker detection. Returns JSON {path, language, hasErrors, comments[{line, endLine, text, kind, marker}]}. marker is TODO|FIXME|FIX|BUG|HACK|NOTE|XXX when the comment contains that UPPERCASE word (case-sensitive, avoids prose false-positives), else null. markersOnly:true returns only marked comments (the debt list). Text clips at 600 chars; list caps at 500. Languages: TS/TSX/JS/JSX/Python. lens is a navigation map over code and docs: use it to LOCATE things, then Read the actual source/section before judging or modifying it. A signature is not the body; an outline is not the section.
Inputs: path (required), markersOnly.
findA
Locate a DEFINITION by name across a directory — "where is X defined?" without grepping. Finds functions, methods, classes, AND non-callable top-level symbols: const/let/var bindings, type aliases, interfaces, and enums (a codebase's source-of-truth often lives in export const …). For text inside markdown docs use search; for who USES a symbol use references. Returns JSON {query, path, matches[{file, name, kind, line, signature, parent}], filesScanned, totalSupportedFiles, truncated, skipped?}. kind is function|method|arrow|getter|setter|class|const|let|var|type|interface|enum|variable. Matching is case-insensitive substring by default; exact:true for exact-name. Unsearchable files are listed in skipped with the reason. Caps: scans up to 300 files, returns up to 100 matches (truncated:true = more exist). Definitions only, not call sites. Languages: TS/TSX/JS/JSX/Python. lens is a navigation map over code and docs: use it to LOCATE things, then Read the actual source/section before judging or modifying it. A signature is not the body; an outline is not the section.
Inputs: name (required), path, exact.
function_bodyA
Verbatim source of ONE function — the focused read. Instead of Reading a whole file, get exactly that function's source (signature + decorators + body). Returns JSON {path, name, parent, kind, signature, line, endLine, async, exported, hasErrors, body}. name matches the bare or dotted-qualified form from functions/find ('Widget.render'); if ambiguous the call FAILS listing candidates with lines — pass the qualified name or line, it never guesses. body is real source, capped at 20000 chars (truncated.bodyChars = true length — Read line..endLine for the rest). The body IS the territory for this one function (reason about its internals) — but re-Read before editing. Languages: TS/TSX/JS/JSX/Python.
Inputs: name (required), path (required), line.
functionsA
Every addressable function in a source file — nested functions, class methods, getters/setters, class-field arrows, namespace members, object-literal methods, default-export functions — with reconstructed signatures. NOT listed: anonymous callbacks, TS overload signatures (only implementations), Python lambdas. Returns JSON {path, language, hasErrors, functions[{name, signature, params[{name,type}], returnType, line, endLine, async, exported, kind, parent}]}. kind is function|method|arrow|getter|setter; parent is the enclosing scope, dotted for nesting ('Widget.render'), null at top level; default exports are named 'default'. Caps at 500 (truncated.functions = true total). Use to pick a line range to Read or read one body with function_body. lens is a navigation map over code and docs: use it to LOCATE things, then Read the actual source/section before judging or modifying it. A signature is not the body; an outline is not the section.
Inputs: path (required).
headingA
Read ONE section of a markdown file instead of the whole file: the referenced heading plus everything under it (subsections included), stopping at the next heading of the same or higher level. ref accepts the exact heading text, its slug ("advanced-usage"), or ANY 1-based line number — a heading line from outline() or a content line from a search hit (resolves to its enclosing section, a note says so). Returns {file, heading, level, startLine, endLine, content}; on multiple matches a note says which was returned and how to pick another. The most token-frugal way to read docs — prefer it over reading whole files.
Inputs: ref (required), file (required).
infoA
Server self-description: version, working directory (the path sandbox root — every path you pass must resolve inside it), supported code languages and doc extensions, the tool list, and every output cap. Returns JSON {name, version, workingDirectory, code:{languages}, docs:{extensions}, tools[], limits, contract}. Read-only, no parameters. Call this first if a path is rejected or to learn what the server can see.
Inputs: no named parameters.
lens_systemA
Install status, self-update, and the current agent guide — lens's self-maintenance tool. action='status' (read-only): running vs on-disk version, git commit, install type, install directory, Node version, and whether an update is available. action='agents_md' (read-only): returns the CURRENT AGENTS.md so you can refresh a stale pasted copy of your operating guide. action='update': dry-run by default (commits behind + incoming changes); apply=true runs update.sh (git pull + npm ci + build + self-test) — new code loads only after the MCP server restarts, and the response says so. force=true stashes local edits. Managed installs (no .git) refuse with guidance. Note: this operates on the lens INSTALL directory, not your project (the code tools' sandbox). Returns JSON.
Inputs: apply, force, action.
linksA
Extract every link from one markdown file: inline text, images (badge constructs yield both image and outer link), [[wikilinks]], , and reference-style [text][id] resolved via their [id]: url definitions. Skips example links inside fenced code blocks. Returns {path, totalLinks, truncated, links:[{type: markdown|image|wikilink|autolink|reference, text, target, line}]} in document order, capped at 200. Map cross-references or collect URLs without reading the file.
Inputs: path (required).
mapA
Whole-project surface in ONE call — the orientation tool over a mixed tree of code AND docs. Walks a directory recursively (skipping node_modules, .git, dist, build, venv, pycache, target, vendor, hidden dirs) and returns JSON {path, code:{files[{path, language, totalLines, classes[], functions[], hasErrors?, error?}], totalSupportedFiles, filesParsed, truncated}, docs:{docs[{path, title, headingCount, outlinePreview[], bytes}], totalDocs, truncated}, summary:{codeFiles, docFiles}}. Code files (.ts .tsx .mts .cts .js .jsx .mjs .cjs .py) report structure; doc files (.md .markdown .mdx) report title + shallow outline. Caps: 200 code files and 200 docs per call (truncated flags carry the true totals — map a subtree to go deeper). Unparseable files appear with an inline error, never vanish. Use FIRST to decide which files matter, then the drill-down tools (overview/functions for code, outline/heading for docs). lens is a navigation map over code and docs: use it to LOCATE things, then Read the actual source/section before judging or modifying it. A signature is not the body; an outline is not the section.
Inputs: path (required).
outlineA
Full heading hierarchy of one markdown file with 1-based line numbers — its table of contents. Recognizes ATX (#…) and setext (===/---) headings; skips headings inside fenced code blocks and YAML frontmatter; handles CRLF. Returns {path, totalHeadings, truncated, headings:[{depth, text, line}], totalLines}; capped at 500. For source code use overview instead. Pick a section here, then read only it with heading(). lens is a navigation map over code and docs: use it to LOCATE things, then Read the actual source/section before judging or modifying it. A signature is not the body; an outline is not the section.
Inputs: path (required).
overviewA
Structural map of a source file: imports, exports, classes (with method names, incl. class-field arrow methods), and top-level functions — each with 1-based line/endLine to jump straight to a Read. Returns JSON {path, language, totalLines, hasErrors, imports[], exports[], classes[{name,line,endLine,methods[]}], functions[{name,line,endLine,exported}]}. hasErrors:true means syntax errors and items may be missing (parseErrors lists offending ranges). Nested functions are in the functions tool. Lists cap at 500 (truncated. holds the true total). Languages: TS/TSX/JS/JSX/Python (Python exports from all). For markdown files use outline instead. Use FIRST to orient in an unfamiliar source file. lens is a navigation map over code and docs: use it to LOCATE things, then Read the actual source/section before judging or modifying it. A signature is not the body; an outline is not the section.
Inputs: path (required).
referencesA
Find who USES a symbol across a directory — the inverse of find. "Who calls X", "what imports it", "where is it used as a type". The reference workflow lens otherwise sends you to grep for, but tree-sitter-backed so a same-named string or comment is never a false positive. Returns JSON {symbol, path, references[{file, line, kind, context}], byKind, filesScanned, totalSupportedFiles, truncated, skipped?}. kind is call | instantiation | import | type-ref | reference | definition (the definition site is included, labelled). context is the source line. Matching is EXACT (references need precision). Caps: scans up to 400 files, returns up to 300 references (truncated:true = more exist — narrow the path). Languages: TS/TSX/JS/JSX/Python. lens is a navigation map over code and docs: use it to LOCATE things, then Read the actual source/section before judging or modifying it. A signature is not the body; an outline is not the section.
Inputs: name (required), path.
searchA
Case-insensitive full-text substring search across MARKDOWN docs (for code symbol definitions use find). Returns matches {path, line, snippet (≤200 chars), inHeading}, heading matches ranked first, capped at max_results (default 50, max 200) with totalMatches and truncated:true when capped. Empty/whitespace queries are rejected (they would match everything). Locate the right doc, then outline/heading to read it.
Inputs: query (required), dir, recursive, max_results.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 13 tools
Each tool targets a distinct aspect of navigation: find for symbol locations, references for usage, functions for file structure, function_body for verbatim source, comments for annotations, and separate doc tools. Descriptions clarify boundaries (e.g., find vs search vs references), leaving no ambiguity.
Tool names are lowercase, mostly single words, but mix verbs (find, search, map) and nouns (comments, overview, links). Underscore appears only in 'function_body' and 'lens_system'. Minor inconsistency but still predictable.
13 tools is well-scoped for a navigation server covering both code and documentation. Each tool serves a clear purpose, and the count feels neither sparse nor overwhelming.
The surface covers locating definitions (find), usage (references), file structure (overview, functions), code reading (function_body, comments), doc navigation (outline, heading, links), full project map (map), plus self-info and maintenance (info, lens_system). No obvious gaps for its stated mission of locating and reading.
Maintenance
Related MCP Connectors
Code-map tools for AI agents: see a repo's structure first, then edit only what matters.
Shared memory MCP server for Claude Code, Codex, Gemini CLI and Cursor. Cited, editable.
Give your AI agent a persistent map of your project's structure, dependencies, and bugs.
Shared memory for teams and their AI agents: Markdown concepts with typed links in shared bundles.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceAn MCP server for semantic code search & navigation that helps AI agents work efficiently without burning through costly tokens. Instead of reading entire files, agents can search conceptually and jump directly to the specific functions, classes, and code chunks they need.121MIT
- AlicenseNot gradedqualityBmaintenanceStandalone MCP server for code structure analysis using tree-sitter. Directory trees, symbol definitions, and call graphs without reading raw source files. Supports Rust, Python, Go, Java, TypeScript, Fortran, JavaScript, C/C++, and C#. Benchmarked up to 68% fewer tokens vs native tools.7Apache 2.0
- AlicenseAqualityDmaintenanceA code-intelligence MCP server for coding agents that combines a static repository map (via tree-sitter) with precise live navigation (via LSP), providing structured, token-budgeted responses anchored to file:line.14MIT
- AlicenseAqualityFmaintenanceA deterministic structural code map server for AI agents, giving them the shape of a codebase (imports, exports, classes, functions, signatures, comments, TODO-markers) without reading whole files into context. Powered by tree-sitter WASM grammars, it runs anywhere Node 18+ works.710 npm1AGPL 3.0