Skip to main content
Glama
semlith

semlith

Official

Semlith

Local search over your code and documents for AI agents.

ci crates.io downloads docs.rs msrv platform mcp license

Install · Quick start · Agents · Portal · Benchmarks · Docs

Why Semlith

An agent working in a large codebase either reads too much, which is slow and expensive, or guesses which file to open, which is often wrong. What it needs is the few spans that answer the question. Semlith finds them.

  • Hybrid search. Every query runs against a vector index (meaning) and a keyword index (exact names), and the two rankings are fused. How does retry backoff work and EMBED_BATCH both land on the right chunk.

  • A code graph built on the same pass. Definitions become nodes, and calls, imports and re-exports become edges, each labelled with how well it is supported. Ask what calls a function, what breaks if it changes, or how two symbols connect.

  • Always current. A watcher re-embeds a file when you save it. There is no build step and nothing to go stale against your working tree.

  • Local by design. No API keys, and no network at query time unless you sign in to Semlith Cloud, a separate hosted service, with semlith cloud login. The portal binds to 127.0.0.1 only. Model weights and acceleration packs are the only downloads, each pinned by digest, and --airgap refuses every outbound request before a connection opens.

  • Made for agents. An MCP server with eight tools listed by default and sixteen in all, registered in your agent clients by semlith setup, and a ledger of every retrieval with the tokens it saved.

  • More than code. PDFs, Office documents, notebooks and Markdown are read as text, and images are searchable by what they show.

Related MCP server: mcp-local-rag

Install

One command, no Rust toolchain needed.

macOS and Linux

curl --proto '=https' --tlsv1.2 -fsSL https://raw.githubusercontent.com/semlith/semlith/main/install.sh | sh

Windows

irm https://raw.githubusercontent.com/semlith/semlith/main/install.ps1 | iex

The installer picks the release for your machine, verifies the ed25519 signature on the release's SHA256SUMS, checks the archive against it, unpacks it into ~/.semlith/bin and runs semlith setup. Setup puts Semlith on your PATH, downloads the embedding model, registers Semlith with each agent client that has a registration CLI, and installs the daemon as a login service. Run semlith setup again at any time to repair an install, and --yes takes every default.

Variable

Effect

SEMLITH_VERSION

Install a specific release tag.

SEMLITH_HOME

Install somewhere other than ~/.semlith.

SEMLITH_NO_SERVICE=1

Skip the login service.

You can also run cargo install semlith, or take an archive from the releases page. semlith upgrade replaces the binary with the newest release, and Semlith never checks for updates on its own.

Requirements. A 64-bit machine running macOS on Apple silicon, Linux on x86_64 or aarch64, or Windows on x86_64 (Windows PowerShell 5.1 is enough). The Linux archives ship libonnxruntime.so beside the binary and need only glibc 2.34 or later and libstdc++ (GLIBCXX_3.4.22). Debian 12, Ubuntu 22.04, RHEL 9 and Amazon Linux 2023 all qualify. Intel macOS is not supported, because ONNX Runtime no longer ships for it.

Verifying the installer

The installer writes the binary to ~/.semlith/bin (mode 700 on macOS and Linux) and, on Linux, libonnxruntime.so beside it. Nothing is written until the archive passes three checks. SHA256SUMS must carry the project's ed25519 signature, checked with ssh-keygen -Y verify (OpenSSH 8.1 or later, which macOS, current Linux distributions and Windows 10 and later have). The archive's SHA-256 must match it. When gh is on your PATH, its GitHub artifact attestation, signed by this repository's release workflow, is checked too. A missing or wrong signature stops the install, and semlith upgrade holds a release to the same key, built into the binary, which semlith --version names. curl is held to https and TLS 1.2 or later, and the script runs only once it has fully downloaded. Releases before v0.40.0 are not signed, so these installers do not install them.

To read the installer before running it:

curl --proto '=https' --tlsv1.2 -fsSLO https://github.com/semlith/semlith/releases/latest/download/install.sh
less install.sh
gh attestation verify install.sh --repo semlith/semlith
sh install.sh

On Windows, run irm https://github.com/semlith/semlith/releases/latest/download/install.ps1 -OutFile install.ps1, read it, then run powershell -ExecutionPolicy Bypass -File install.ps1.

To verify an archive by hand:

echo 'semlith-release ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICMTHiHMzeSHzzc+8UDZHUzJcvb6XGUBb+TJdFes91xm' > allowed_signers
ssh-keygen -Y verify -f allowed_signers -I semlith-release -n file -s SHA256SUMS.sig < SHA256SUMS
grep "  semlith-<tag>-<target>.tar.gz$" SHA256SUMS | shasum -a 256 -c
gh attestation verify semlith-<tag>-<target>.tar.gz --repo semlith/semlith

Quick start

cd ~/src/ripgrep
semlith index .                                   # one store, and the model (~52 MB) downloads once
semlith search "where is a file detected as binary"
semlith neighbors search_parallel                 # callers and callees
semlith start                                     # the daemon and the portal
$ semlith search "where is a file detected as binary" -k 1
1. 0.083 vf  GUIDE.md:735-746
   binary files:

   1. The default mode is to attempt to remove binary files from a search
      completely. ...

$ semlith neighbors search_parallel
callers (2)
  main via defines (extracted)  crates/core/main.rs:44  · call at line 166
  run via calls (resolved)  crates/core/main.rs:78  · call at line 90
callees (25)
  matcher via calls (resolved)  crates/core/flags/hiargs.rs:379  · called at crates/core/main.rs:177
  search_worker via calls (resolved)  crates/core/flags/hiargs.rs:705  · called at crates/core/main.rs:176
  ...

Every hit is a path:start-end locator you can hand to an editor or an agent, and vf says the vector and keyword lists both found it. Documents answer the same way.

Use it from an agent

semlith setup     # registers each agent client that has a registration CLI
semlith doctor    # checks each client can actually reach it, and says how to fix it if not

semlith setup registers the MCP server at user scope in each installed client that has a registration CLI, which is six of the 12 documented clients. The other six are a config file, which semlith setup --register-all writes for you, and semlith doctor reports on every one. Each stanza is run against the binary in tests/clients.rs, and docs/clients.md has them all with the HTTP transport. Setup also installs the Semlith Agent Skill, a steering hook that suggests a Semlith call when an agent is about to grep, and a read-only research agent.

semlith mcp serves MCP over stdio, and the daemon serves the same tools over HTTP at /mcp, behind an agent key (semlith key show). Eight tools are listed by default, because every definition costs tokens on every request, and SEMLITH_MCP_TOOLS=all lists all sixteen.

Tool

Answers

semlith_search

Where is the answer? Path, span, enclosing symbol and how it was found.

semlith_brief

Everything one question needs in one call, with spans, text and one-hop callers and callees, under a token budget.

semlith_read

One span or one symbol, nothing around it.

semlith_symbol

Where is this defined? history: true gives what it used to be.

semlith_neighbors

What calls it, and what it calls.

semlith_impact

Everything that reaches it, and the files involved.

semlith_files

Which files are indexed.

semlith_stats

What each store holds.

semlith_path, semlith_trace

How two symbols connect, as a chain or as evidence.

semlith_pattern

A tree-sitter structural pattern over one language.

semlith_index, semlith_add, semlith_forget

Change a store from inside a conversation.

semlith_report

One of five reports from the ledger, index and graph.

semlith_languages

The language names lang accepts.

A bare semlith mcp opens every registered store, so one question can span repositories. It speaks MCP 2026-07-28, 2025-11-25, 2025-06-18 and 2024-11-05, each proven by a session in tests/mcp.rs. In the official MCP registry it is listed as mcp-name: io.github.semlith/semlith

The portal

semlith start runs one daemon that owns every store. It holds each store's write lock, watches its folders, re-embeds what you save, and serves a portal at http://127.0.0.1:7365. The URL with its session token is printed once, on stdout. docs/portal.md documents every page.

It is not on the network. The daemon binds 127.0.0.1 only, with no flag to change it. Every page and /api/ route needs the per-run session token, sent in a Semlith-Token request header, and without it the answer is 401. The agent key opens /mcp and nothing else. Responses carry a Content-Security-Policy of 'self', no CORS header is sent, and every byte the portal loads is compiled into the binary. --airgap refuses every outbound request before a connection opens, which covers model and pack downloads that are not cached, semlith upgrade, the price table, Semlith Cloud and semlith add. The Privacy page checks these claims against the running daemon, and docs/security.md is the full account.

How it works

files ──chunk──> text ──embed──> vectors ──quantize──> index/*.tvim  (turbovec)
                  │
                  └──parse──> symbols, edges ─────────> store.db      (SQLite)

query ──embed──> vector ──search shards──┐
      ──keywords──> FTS5 ────────────────┴──fuse──> ranked spans ──> enclosing symbol
  • Reading. Files are read as the text a person would see. Thirteen formats have their own reader (PDF, Word, PowerPoint, Excel, notebooks and more). Everything else is read as UTF-8 and cut into line-aligned chunks of up to 800 characters. A Markdown chunk is embedded with its heading path, a code chunk with its enclosing definition.

  • Embedding. granite-embedding-small-english-r2 (int8, 384 dimensions, ~52 MB) through ONNX Runtime, fixed per store. The daemon also embeds on the Neural Engine on Apple silicon and on the GPU (Core ML, WebGPU or CUDA), each lane checked against known answers before its first batch. Images go through CLIP ViT-B/32 into a second vector space and match by what an image shows, not by OCR.

  • Storing. index/ holds turbovec shards of 65 536 quantized vectors, and store.db holds chunk text, spans, symbols, edges, the ledger and the content hashes that make re-indexing incremental. A save rewrites one shard.

  • Graph. All 46 languages carry edges. Each edge is extracted (settled by the file itself), resolved (one candidate), ambiguous (several, and it says how many) or inferred (a bare name match). semlith path refuses to cross a name it cannot pin down unless you pass --all-edges.

docs/architecture.md is the full account.

Commands

Command

Does

semlith index [PATHS...]

Index paths into a store; re-running embeds only what changed. --each gives each path its own store, --projects <DIR> one per repository.

semlith watch [PATHS...]

Re-embed files as they are saved.

semlith start

The daemon: every store kept current, the portal and /mcp on 127.0.0.1:7365.

semlith search <QUERY>

Hybrid search. -k, --json, --path/--ext/--lang (repeatable; a leading ! excludes), --prefer code|docs, --exact for regex lines with their definition.

semlith brief <QUESTION>

Spans, their text and one-hop callers and callees, under --budget tokens (default 4000).

semlith read <TARGET>

One span (src/store.rs:1041-1080) or one symbol.

semlith symbol, neighbors, impact, path, trace

The graph: definitions, callers and callees, blast radius, chains, evidence.

semlith pattern <QUERY> --lang L

A tree-sitter structural pattern.

semlith stats, files, languages, models

What a store holds; files --tree shows what was not indexed and why.

semlith refused, scan

Files not indexed and why; files held that today's rules would refuse.

semlith add <URL>

Fetch one https URL into a store. No crawling, no credentials, no private addresses.

semlith forget, compact, drop

Remove a file, reclaim dead bytes without re-embedding, delete a store.

semlith adopt, trust

Move a store into the store home; allow one outside it.

semlith ledger, report, schedule, prices

Retrieval history, five reports in five formats, scheduled reports, the price table.

semlith mcp, hook, key

MCP over stdio, the steering hook, the HTTP agent key.

semlith setup, doctor, upgrade

Install and repair, diagnose each client, replace the binary.

semlith accel

Which devices embed: CPU (always on, SEMLITH_CPU_CAP caps it), Neural Engine, GPU, and the experimental CUDA, TensorRT, OpenVINO and llama.cpp lanes.

semlith cloud

Connects to Semlith Cloud, a separate hosted service. Nothing contacts the network until semlith cloud login.

semlith <command> --help documents every flag.

Where stores live. Each store lives in ~/.semlith/stores/<name> and is listed in ~/.semlith/registry.json, so semlith mcp and semlith start find every store with no flags. A command picks its store from --store or SEMLITH_STORE, then a .semlith beside the corpus, then the registered store covering the current directory, and otherwise creates one.

What gets indexed. Everything under the given paths except .gitignored and hidden files, binaries, files over 8 MiB, archives that expand past 32 MiB, and generated or vendored directories. node_modules is always skipped, and target, build, dist and vendor are skipped beside their manifest unless SEMLITH_DEFAULT_IGNORES=0 is set. Credentials are never indexed, and the grey zone is yours to decide.

The retrieval ledger

Every retrieval is recorded in its store, whether it came from an agent over MCP, the CLI or the portal. A row holds the query, the client, the session, the hits and what was read. Each row carries the hash of the row before it, so semlith ledger --verify finds an edited or deleted row. It is local, on by default, and off with --no-ledger or SEMLITH_LEDGER=0.

What it saved on this repository. Ask semlith brief all 107 questions of this repository's retrieval harness against a store of src/, then run semlith ledger --verify. An answered question sent 3 982 tokens to the agent on average, against 105 132 for reading the 4.1 files those answers named, whole. That is 26× less, at 100 % coverage, counted with the store's own tokenizer. It is an upper bound, because a file named by two answers is counted twice.

Benchmarks

Asked 127 questions about 53 public documents, an Opus agent with Grep, Glob and Read found the passage 63 % of the time, and 41 of its 127 top quotes appear nowhere in the document it named. It found the file by name, could not read it, and wrote something plausible. With Semlith installed it found 98 %, and 0 of 126 quotes were made up. On single-repo code with a strong model it does not help, and Opus scores 92 % with or without it.

Public code-retrieval benchmarks, run by bench/scorecard/ on one M1 MacBook Air in October 2026 and published as measured, including where another tool wins. Every table, every chart and the command that reproduces each number are on the scorecard page.

Benchmark

semlith

baseline

instances

SWE-bench Verified, right file in the top 10

76.8 %

47.0 % grep then read

500

SWE-bench Lite, right file in the top 10

72.3 %

47.3 % grep then read

300

SWE-bench Verified, median tokens to the first right file

224

15 491 grep then read

500

CodeRAG-Bench DS-1000, NDCG@10

0.260

0.158 BM25

513

Agents (Opus), cost per correct answer

$0.065

$0.072 grep only

50 × 3 runs

Documents (PDF, Office, EPUB, …), passage found within 4k tokens

116/127

34/127 rga

127

Agents (Opus) on those documents, passage found

124/127

80/127 grep only

127

Real sessions, tokens sent against whole files read

75.2× fewer

1,575 retrievals

Semlith does not win everywhere. Semble edges it at hit@1 and hit@10 on the 20-instance competitor sample, BM25 beats it on CodeRAG-Bench's RepoEval, and with Grep built in, Opus rarely reaches for Semlith on its own. The scorecard page has these rows and RepoBench-R.

The numbers

Coverage, read out of the code. tests/readme.rs fails the build if any of these drifts from its source.

languages searchable and graphed

46

edge kinds

6

document formats with a reader

13

image types

5

MCP tools

16

CLI commands

36

agent clients, each setup stanza run against the binary in tests/clients.rs

12

prebuilt targets

4

Measured on a 4P+4E Apple silicon laptop, with the command that reproduces each. docs/performance.md has the full tables.

warm search at 700 / 7 000 / 70 000 chunks, peak RSS under 240 MB

16.2 / 37.9 / 159.1 ms

cargo test --release --test measure -- --ignored --nocapture

edit on disk to searchable, watcher running

1.24 s

docs/performance.md

indexing on an M1 Air: Neural Engine / CPU and GPU through Core ML

229.8 / 62.7 chunks/s p10

docs/performance.md

daemon memory 60 s after a run, seven stores open

465 MB

footprint -p <pid>

idle watcher CPU over 60 s

under 1.0 s

the same

one changed file

1 shard rewritten

cargo test --release --test shards -- --ignored --nocapture

tools/list

2 633 bytes, eight tools; 1 323 tokens added to an Opus request

cargo test --release --test retrieval -- --ignored

retrieval, 30 sealed questions of 107

hit@1 24/30, hit@3 27/30, hit@8 29/30, zero spread; identifiers 11 of 11 in the top three

the same

one search, rescoring off / on

8.2 ms / 132.2 ms on a 300-file store

the same

one answered question, brief against search-then-read

1.00 calls vs 2.54, 1 112 tokens vs 725

the same

call-edge resolution

62 % settled

the same

the macOS arm64 binary

125 435 312 bytes (119.6 MiB)

ls -l target/release/semlith

the Linux glibc floor

GLIBC_2.34

objdump -T semlith runtime/libonnxruntime.so

Peak memory does not grow with the corpus, so plan for about 600 MB whatever you index. Query latency does, because the vector scan is linear.

Known limits

  • One writer per store. While the daemon or semlith watch holds a store, a second semlith index against it exits and names the holder.

  • First indexing is bound by the model, at about 230 chunks/s on an M1's Neural Engine, 63 on its CPU and GPU, 30 on the CPU alone. Later runs embed only what changed. On macOS the CPU lane alone runs at about 60 % of a terminal's speed inside the login service, because of how launchd schedules agent threads.

  • On a Mac the GPU does not embed beside the Neural Engine unless you run semlith accel on gpu-beside-ane. CUDA runs on Linux x86_64 only, and an NVIDIA card on Windows embeds through WebGPU.

  • A new binary on macOS asks again for access to folders such as Documents, and a store there waits, showing running, until the prompt is answered.

  • A store larger than SEMLITH_INDEX_MEMORY reads shards from disk on every query, so raise the budget if the store fits in memory.

  • The default model is English-only and fixed when a store is created. Image search is not OCR.

  • Search filters are SQLite GLOB (no regex), and --lang maps extensions and never reads contents. Multi-store search merges rankings rather than ranking jointly. Cross-encoder rescoring (SEMLITH_RERANK=on) is off by default, because it costs about 16× the search time for two more answers in 77 at k=1.

  • Binaries are not notarised by Apple or Authenticode-signed for Windows. Each release carries an ed25519-signed SHA256SUMS and a GitHub artifact attestation instead. There is no ARM64 Windows or Intel macOS build, and semlith upgrade only replaces a binary in ~/.semlith/bin.

Documentation

Document

Covers

docs/portal.md

Every page and control of the portal.

docs/clients.md

Setup stanzas for 12 agent clients and the HTTP transport.

docs/architecture.md

How the pieces fit together, and why.

docs/performance.md

Every measured number, with its command and date.

docs/models.md

The embedding models, and how to choose one.

docs/security.md

The threat model and what is on the wire.

docs/compatibility.md

What is a contract, and what a 0.x version promises.

CHANGELOG.md

What changed in each release.

Contributing

Contributions are welcome. CONTRIBUTING.md covers building, the checks CI runs and what is out of scope, and AGENTS.md is the same for a coding agent. Everyone follows the Code of Conduct. To report a security problem, see SECURITY.md rather than opening an issue.

Prior art

Semlith's vector index is turbovec by Ryan Codrai (MIT), an implementation of TurboQuant from "TurboQuant: Online Vector Quantization with Near-optimal Distortion Rate" by Amir Zandieh, Majid Daliri, Majid Hadian and Vahab Mirrokni. TurboQuant is data-oblivious. It rotates each vector randomly and quantizes each coordinate on its own, with no training pass. That is what an index refreshed on every file save needs. There is nothing to gather before the first file and nothing to rebuild as the corpus grows, and a vector added is a vector searchable.

License

Apache-2.0. See LICENSE and NOTICE.

Model weights are downloaded at runtime under their own licenses. The default, ibm-granite/granite-embedding-small-english-r2, is Apache-2.0, and the CLIP ViT-B/32 pair, the WebGPU plugin (MIT) and the NVIDIA libraries the CUDA pack fetches carry theirs. See docs/models.md.

Available Tools

8 tools
semlith_briefD
Read-only

Spans, the top span's text and its callers/callees, in one call.

NameRequiredDescriptionDefault
pathNo
storeNo
questionYes

TDQS

D1.6/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true, so the safety profile is covered. The description adds only that results include spans, top span text, and callers/callees, but it does not explain the tool's behavior, aggregation logic, or response shape beyond that fragment.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness2/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is very short but under-specified rather than concise. It is not front-loaded with a clear action, and its noun-phrase fragment leaves essential information unstated.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness1/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, 0% parameter coverage, and only a readOnly annotation, the description is far too thin to support correct invocation. It does not explain what a "brief" contains, how question/path/store affect results, or when this tool should be chosen.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters1/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, and the description does not mention any of the three parameters (question, path, store). It therefore provides no meaning for the required question parameter or the optional path/store filters.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose2/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description names some returned content (spans, top span text, callers/callees) but uses a fragment rather than a clear verb and does not explain what the tool actually does. It does not distinguish its purpose from siblings like semlith_symbol or semlith_neighbors.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines1/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

There is no guidance on when to use semlith_brief versus alternatives such as semlith_symbol, semlith_neighbors, or semlith_impact. The phrase "in one call" hints at aggregation but does not state when that is preferable.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

semlith_filesB
Read-only

What is in this folder; tree: true for a directory view with symbols.

NameRequiredDescriptionDefault
pathNo
treeNo
storeNo

TDQS

B3/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The readOnlyHint annotation already establishes that this is a safe read operation. The description adds that `tree: true` produces a directory view with symbols, but it does not disclose return shape, recursion behavior, or how `path` and `store` affect results.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single compact sentence with no filler. It front-loads the core resource question and appends the `tree` behavior efficiently.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The tool has three parameters with no schema descriptions and no output schema. The description covers only the `tree` flag, leaving the meaning of `path`, `store`, and the expected return values unclear.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, and the description only explains the `tree` parameter. The `path` and `store` parameters are undocumented in both the schema and the description, leaving significant ambiguity for a 3-parameter tool.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly identifies the resource as folder contents and hints at a directory-tree view. It does not explicitly distinguish the tool from siblings like semlith_search or semlith_read, so it falls short of a 5.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The only guidance is the note that `tree: true` gives a directory view with symbols. There is no explanation of when to use this tool versus semlith_search, semlith_read, or other siblings, and no exclusions or prerequisites.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

semlith_impactB
Read-only

What breaks if X changes: every call site, transitively. Type::method narrows.

NameRequiredDescriptionDefault
nameYes
pathNo
storeNo

TDQS

B3.1/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

readOnlyHint=true is consistent with a pure analysis tool, and the description adds useful scope information (transitive traversal, Type::method narrowing syntax) beyond the annotation. It says nothing about which store/index is consulted, whether results include indirect dynamic dispatch, or how missing symbols behave.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two short clauses, front-loaded with the core question the tool answers, with no filler. It is arguably over-compressed to the point of being telegraphic, but nothing is wasted.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 3-parameter tool with no output schema, the description does not say what the result looks like (list of call sites? edges? grouped by file?), how store/path change the search space, or what happens when the symbol is ambiguous. An agent would have to guess on several call-shaping details.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0% for 3 parameters. The description only implicitly maps "X" to the required name argument and hints at the "Type::method" narrowing format; path and store are completely unexplained in both the schema and the description.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The phrase "What breaks if X changes: every call site, transitively" names a concrete analysis (reverse-dependency / impact) and its scope (transitive call sites), which separates it from semlith_read and semlith_symbol. It never names a sibling explicitly, so differentiation is inferred from the word "breaks" rather than stated.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The framing "What breaks if X changes" implies a pre-refactor / before-you-change-something usage context, which is real guidance. There is no explicit statement of when to use this over semlith_neighbors or semlith_symbol, and no exclusions or prerequisites.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

semlith_neighborsC
Read-only

Who calls X, and what X calls. Use for caller questions.

NameRequiredDescriptionDefault
nameYes
pathNo
storeNo

TDQS

C2.7/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The readOnlyHint annotation already declares this is a safe read, so the description carries no safety burden. Beyond that the description discloses nothing useful: no traversal depth, no directionality handling, no behavior when the symbol is ambiguous or missing, and no indication of result size.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two short sentences, front-loaded with the core semantic relation and followed by the usage cue. No filler and nothing buried; brevity is appropriate here, though it tips into under-specification.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, no annotation detail beyond readOnly, zero parameter documentation, and several graph-traversal siblings, this description does not supply enough for an agent to call correctly. The critical unknowns — what `path` and `store` mean and what the response contains — are unaddressed.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0% across three parameters, so the description must compensate and does not. 'X' loosely implies the required `name`, but `path` and `store` are entirely unexplained in both schema and description, leaving two-thirds of the input surface opaque.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose3/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a recognizable resource relation — 'Who calls X, and what X calls' — which conveys a bidirectional call-graph neighborhood lookup. However, it never differentiates from the closely related sibling semlith_impact (also a graph traversal) or semlith_symbol, so an agent must guess which one to reach for.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

'Use for caller questions' gives one implied trigger, which is more than nothing. But there is no exclusions guidance, no mention of when to prefer semlith_impact (transitive impact) or semlith_symbol, and no prerequisites or scope limits.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

semlith_readC
Read-only

Read path:start-end, path:line, or a symbol (Type::method) whole.

NameRequiredDescriptionDefault
storeNo
targetYes

TDQS

C2.9/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The readOnlyHint annotation already establishes that this is a safe read operation, and the description is consistent with that. However, it adds no behavioral context about return format, error handling, truncation, or what 'whole' means for a symbol, leaving gaps beyond the annotation.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single front-loaded sentence with no wasted words. The compressed syntax is efficient, though the phrase 'or a symbol (Type::method) whole' is slightly awkward to parse.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

There is no output schema and no explanation of what is returned, such as source text, numbered lines, or symbol bodies. For a tool with two parameters and zero schema coverage, the description should also clarify the store parameter and distinguish this read from semlith_symbol.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description carries the full burden, and it does explain the accepted syntax for the required target parameter. It never mentions the optional store parameter or its meaning, leaving a significant undocumented input.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a clear read operation with the specific target forms it accepts: path:start-end, path:line, and Type::method. It does not differentiate itself from sibling tools such as semlith_symbol, leaving ambiguity about when a symbol should be read here versus via the symbol-specific tool.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It lists accepted target formats but gives no explicit guidance on when to use this tool instead of siblings like semlith_search, semlith_symbol, or semlith_files. The agent must infer usage from the input syntax alone.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

semlith_statsC
Read-only

What is indexed, per store.

NameRequiredDescriptionDefault

No parameters

TDQS

C2.4/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

readOnlyHint=true covers the safety profile, so the bar is lower, but the description adds essentially nothing beyond it: no output shape, no granularity, no note on cost or freshness of the index data. The one clause provided is a vague pointer at the return value rather than context an agent couldn't guess.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single short fragment with no padding, so nothing is wasted, but the brevity comes from under-specification rather than economy. It is front-loaded only in the sense that it's a sentence fragment.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description carries the full burden of explaining what comes back, and "per store" plus "what is indexed" is far too thin to tell an agent the shape, units, or meaning of 'store'. For an introspection tool whose whole value is its return value, this is a real gap.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes zero parameters, so the baseline is 4; the description has no parameters to explain and correctly doesn't invent any. Nothing here misleads about inputs.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose2/5

Does the description clearly state what the tool does and how it differs from similar tools?

The fragment "What is indexed, per store" hints at a stats/coverage report grouped by store, but there's no verb and no named resource, so it reads close to a restatement of the tool name 'stats'. It does not distinguish this from siblings like semlith_files or semlith_symbol, which also concern indexed content.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

There is no when-to-use guidance, no statement of alternatives, and no indication of what question this tool answers that the other semlith_* tools don't. The agent must infer everything from the name.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

semlith_symbolC
Read-only

Where X is defined, with callers and callees. names: up to 20, a row each.

NameRequiredDescriptionDefault
nameNo
pathNo
namesNo
storeNo

TDQS

C2.4/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations declare readOnlyHint=true, so safety is covered, but the description adds little beyond it: no statement of what is returned when a symbol is missing, whether callers/callees are scoped to a workspace or store, or how the store/path interplay affects lookup. "A row each" hints at output shape but is unexplained.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

It is two short sentences with no padding, so length is fine, but the structure is telegraphic and the most important element (what is being located) is abbreviated to "X," which front-loads ambiguity rather than clarity.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 4-parameter tool with zero schema coverage and no output schema, the description should carry nearly everything, yet it omits parameter meaning, return shape (beyond "a row each"), and behavior when a symbol is absent or ambiguous. An agent could not reliably call this without guessing at path/store semantics.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0% for all four parameters (name, path, names, store), so the description must compensate and largely fails. It clarifies that names takes up to 20 entries and that the pair is singular/plural, but says nothing about path vs store or how they scope the lookup, leaving the two most ambiguous parameters undocumented.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose3/5

Does the description clearly state what the tool does and how it differs from similar tools?

The phrase "Where X is defined, with callers and callees" does convey a specific capability (definition lookup plus call graph), but the tool name is opaque (semlith_symbol) and the description never states the resource plainly (symbol/function definition). It is distinguishable from semlith_search or semlith_read only by inference, so it sits at vague-but-inferable rather than clear.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

There is no explicit when-to-use guidance and no mention of alternatives, despite siblings like semlith_neighbors and semlith_impact that plausibly overlap with caller/callee information. The only usage hint is buried in the fragment "names: up to 20, a row each," which tells batch size, not selection criteria.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 8 tool updates
    • First observedsemlith_brief
    • First observedsemlith_files
    • First observedsemlith_impact
    • First observedsemlith_neighbors
    • First observedsemlith_read
    • First observedsemlith_search
    • First observedsemlith_stats
    • First observedsemlith_symbol

TDQS

C2.8/5.0

Scored across 8 tools

Disambiguation3/5

Three tools — semlith_brief, semlith_neighbors, and semlith_symbol — all return callers/callees, and an agent could easily misselect among them; the descriptions distinguish them only subtly (summary vs. caller questions vs. batch definitions). semlith_search and semlith_read also overlap on 'where is X / show me X', and semlith_impact is close to a transitive version of neighbors. The boundaries are inferable but require careful reading of every description.

Naming Consistency4/5

All eight tools share a uniform semlith_ prefix with lowercase single-word snake_case names, so there is no stylistic mixing. The pattern is noun/topic-based (brief, impact, neighbors, symbol) rather than a strict verb_noun scheme, and a couple of names (brief, impact) are abstract enough that the name alone doesn't convey the operation.

Tool Count5/5

Eight tools is well within the ideal range for a code-intelligence server, and each maps to a recognizable query type: search, read, structure, definitions, callers, impact, and index metadata. Nothing appears redundant enough to cut, and no obvious capability is missing a slot.

Completeness4/5

The surface covers the read-side lifecycle well — discovery (search), structure (files), navigation (read, symbol), analysis (neighbors, impact), and introspection (stats). Minor gaps exist: no explicit list of all symbols/top-level entities in a store, no cross-store or repo-level overview, and no history/diff-style view, though an agent can work around these via search and files.

Maintenance

ActivityActive
ResponsivenessUnresponsive

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    C
    maintenance
    A fully local RAG MCP server for semantic code search and code intelligence, using AST-level chunking and hybrid search to pinpoint functions, classes, and APIs. No cloud, no API keys, zero setup.
    16
    47 npm
    13
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    Local-first code intelligence MCP server that enables coding agents to search code, inspect structure, read exact ranges, and explore Git history with explicit token budgets.
    121
    Apache 2.0
  • A
    license
    A
    quality
    A
    maintenance
    Local code search for AI agents over a tree-sitter index of your repo: exact symbol lookup, scope-aware usages, call graph and impact analysis, AST pattern search, indexed grep and semantic search. 28 MCP tools, 19 languages, runs fully offline.
    3
    28
    12
    MIT