Skip to main content
Glama
Furkiozknn

local-notes-search-mcp

by Furkiozknn

πŸ”Ž local-notes-search-mcp

Semantic search over your own files β€” as an MCP server.

Ask questions in plain language instead of guessing the exact keyword you typed six months ago.

CI Tests License: MIT Python MCP

No API key Offline No server Optional LLM Storage Embeddings

πŸ‡ΉπŸ‡· TΓΌrkΓ§e README β†’


✨ What it does

Point it at a folder β€” project notes, a scattered Claude projeler/ tree, a docs directory β€” and search it by meaning, not by exact string match.

β–Έ index_directory("C:/Users/you/Desktop/notes")
  βœ… 128 files indexed Β· 941 chunks Β· 12 unchanged (skipped)

β–Έ search_notes("what did I decide about the auth redesign?")
  🎯 3 results:

  ── notes/2026-08-decisions.md:12-24  Β·  distance 0.31 ──────────────
     ## Auth redesign
     Decided: session-based instead of JWT, because the refresh-token
     rotation story was getting worse than the problem it solved...

  ── notes/meeting-2026-07-30.md:88-101  Β·  distance 0.44 ────────────
     ...agreed to revisit auth after the billing migration ships.

Nothing in that flow touched the network. No OpenAI key, no Pinecone account, no Docker container, no docker compose up before you can search your own notes.

Want a synthesized answer instead of a result list? πŸ’‘ ask_notes runs the exact same retrieval, then has an LLM answer grounded only in the retrieved chunks, with file:line sources attached. It's strictly opt-in β€” set GROQ_API_KEY or MISTRAL_API_KEY and it synthesizes; set neither and it quietly returns the raw matches instead of failing.


Related MCP server: code-rag

🧭 The 30-second pitch

grep / ripgrep

Cloud RAG SaaS

local-notes-search-mcp

Finds "the auth decision" when you wrote "session vs JWT"

❌

βœ…

βœ…

Works with no API key

βœ…

❌

βœ…

Your files never leave the machine

βœ…

❌

βœ…

No server / daemon / container to run

βœ…

❌

βœ…

Answers with exact file:line you can jump to

βœ…

⚠️

βœ…

Costs money per query

βœ… free

❌

βœ… free

Usable directly by Claude / any MCP client

❌

⚠️

βœ…

Optional grounded LLM answer with sources

❌

βœ…

βœ… opt-in


πŸ—οΈ How it works

flowchart LR
    subgraph INDEX["πŸ“₯ Index pipeline β€” runs when you ask it to"]
        direction LR
        A["πŸ“ Local folder"] --> B["🚢 Walk + filter<br/>skip .git, node_modules,<br/>.venv, files &gt; 2MB"]
        B --> H{"πŸ” Content hash<br/>changed?"}
        H -- "no" --> SKIP["⏭️ Skip<br/>zero CPU"]
        H -- "yes" --> C["βœ‚οΈ Line-based chunker<br/>1500 chars + 200 overlap<br/>never splits a line"]
        C --> D["🧠 fastembed ONNX<br/>paraphrase-multilingual-MiniLM-L12-v2 · 384-d"]
    end

    D --> DB[("πŸ—„οΈ sqlite-vec<br/>vec0 virtual table<br/>~/.local-notes-search/index.db")]

    subgraph QUERY["πŸ” Query path β€” 100% offline"]
        direction LR
        Q["πŸ’¬ Natural-language<br/>question"] --> QE["🧠 Embed query<br/>same model"]
    end

    QE --> DB
    DB --> R["🎯 Top-k chunks<br/>file:line + snippet<br/>+ distance score"]

    R -. "opt-in: ask_notes<br/>needs an API key" .-> LLM["πŸ€– LLM synthesis<br/>Groq β†’ Mistral fallback<br/>grounded in retrieved chunks only"]
    LLM --> ANS["πŸ’‘ Answer + file:line sources"]

    style LLM fill:#1c1730,stroke:#a371f7,color:#ffffff
    style ANS fill:#1c1730,stroke:#a371f7,color:#ffffff
    style DB fill:#003b57,stroke:#00b4d8,color:#ffffff
    style R fill:#1a7f37,stroke:#3fb950,color:#ffffff
    style SKIP fill:#4d3800,stroke:#d4a72c,color:#ffffff

🧰 MCP tools

πŸ› οΈ Tool

What it does

πŸ—‚οΈ index_directory(path, extensions=None)

Recursively indexes a directory. Skips .git / node_modules / .venv / __pycache__ / dist / build and anything over 2 MB. Unchanged files are skipped via a cheap hash check; deleted files are purged from the index.

πŸ” search_notes(query, top_k=5, path_prefix=None)

Natural-language semantic search. Returns file:line-range + snippet + distance score β€” not just a bag of filenames. path_prefix scopes the search to one subtree.

πŸ’‘ ask_notes(question, top_k=5, path_prefix=None)

Optional. Same retrieval as search_notes, then an LLM (Groq β†’ Mistral fallback) answers using only those chunks, followed by a file:line source list. Needs GROQ_API_KEY or MISTRAL_API_KEY. With neither key set β€” or if the provider chain fails β€” it degrades to returning the raw matches with a note. It never hard-fails just because synthesis wasn't possible.

πŸ“‹ list_indexed_files(path_prefix=None)

What's in the index right now: path, chunk count, last-indexed timestamp. Useful before searching, or to debug a stale result.

🧹 remove_directory(path)

Drops everything under path from the index. Does not delete your files β€” it only cleans the index.

πŸ”’ index_directory, search_notes, list_indexed_files and remove_directory require no API key and make no network calls at all. ask_notes is the one tool that can talk to a remote provider, and only when you explicitly give it a key.


πŸš€ Quickstart

git clone https://github.com/Furkiozknn/local-notes-search-mcp.git
cd local-notes-search-mcp
uv sync

Register local_notes_search.py as a stdio MCP server:

{
  "mcpServers": {
    "local-notes-search": {
      "command": "uv",
      "args": [
        "--directory", "/absolute/path/to/local-notes-search-mcp",
        "run", "local_notes_search.py"
      ]
    }
  }
}

On the first index_directory / search_notes call, the fastembed model (~130 MB) is downloaded once and cached locally. Every call after that is fully offline.

Env var

Default

What it does

LOCAL_NOTES_SEARCH_DB

~/.local-notes-search/index.db

Where the index lives. One single file for every indexed directory β€” so a single search_notes call can span all your project folders at once.

LOCAL_NOTES_SEARCH_ALLOWED_ROOTS

unset

Optional allowlist. When set, index_directory refuses any path that does not resolve inside one of these directories. os.pathsep-separated (: on Linux/macOS, ; on Windows).

GROQ_API_KEY

unset

Optional. Enables ask_notes synthesis via Groq (first in the provider chain).

MISTRAL_API_KEY

unset

Optional. Fallback provider for ask_notes when Groq is unset or fails.

Keys are read from the environment only β€” never commit them, and never put them in the MCP client config file you check into git.

Default indexed extensions: .md .txt .py .js .ts .tsx .jsx .json .yaml .yml .rst .toml β€” override per call with extensions=[...].

πŸ”’ What can be indexed

index_directory reads whatever it is pointed at, and ask_notes sends the chunks it retrieves to a third-party LLM (Groq or Mistral) when a key is configured. So an indexed path is a path whose contents can leave the machine. Two guards exist:

1. LOCAL_NOTES_SEARCH_ALLOWED_ROOTS (opt-in). Unset by default β€” that is the historical behaviour, any directory the running user can read is indexable, and this project does not pretend an empty default is a sandbox. Set it and index_directory refuses anything outside:

export LOCAL_NOTES_SEARCH_ALLOWED_ROOTS="$HOME/notes:$HOME/projects"

Paths are resolved (.. collapsed, symlinks followed) before the check, and a subdirectory of an allowed root is allowed. A configured entry that is not a directory is an error rather than being silently dropped β€” a typo must not quietly switch the allowlist off.

2. A credential-filename denylist (always on). These are never indexed, whatever the allowlist or the extensions=[...] argument says:

.env Β· .env.* Β· .netrc Β· _netrc Β· id_rsa Β· id_dsa Β· id_ecdsa Β· id_ed25519 Β· credentials.json Β· *.pem

Matching is case-insensitive. It is a name denylist, not a secret scanner: it stops the obvious cases (credentials.json would otherwise sail through the default .json extension filter), not a key pasted into a .md file.


🧠 Why this architecture

Decision

Why

πŸ—„οΈ sqlite-vec (Apache-2.0) for vector storage

A vec0 virtual table inside one ordinary .sqlite file β€” no daemon, no Docker, no hosted service. Qdrant and pgvector were evaluated and rejected specifically because both need a running server process. A personal notes index should not require ops.

⚑ fastembed (Apache-2.0), not sentence-transformers

Local embedding here is the only path β€” it runs on every index and every search. sentence-transformers drags in torch (~1 GB); that's an acceptable price for a rarely-hit fallback, but not for the hot path. fastembed's quantized ONNX models land around 100–150 MB with no torch at all. A deliberate divergence, documented in the module docstring.

🧬 paraphrase-multilingual-MiniLM-L12-v2, 384 dims

Small (0.22 GB), Apache-2.0, and β€” decisive for this tool β€” actually multilingual: the previous bge-small-en-v1.5 was an English-only model quietly embedding Turkish notes. Symmetric, so queries and passages embed identically. Override with LOCAL_NOTES_SEARCH_MODEL; a mismatched existing index is refused, never silently compared. No GPU required.

βœ‚οΈ Line-based chunking, no NLP/AST dependency

Chunks accumulate whole lines until a character budget is hit β€” a line is never split in half, so every file:line reference the tool returns is exact. An overlap window keeps context alive across boundaries. Deterministic, and fully unit-testable without loading the embedding model.

πŸ” Whole-file content-hash skip on re-index

index_directory is designed to be re-run constantly. Re-embedding unchanged files would burn CPU on every single call for zero benefit β€” one cheap hash comparison avoids it.


πŸ§ͺ Tests

uv run pytest -v

50 tests, on a deliberate two-tier strategy. Pure-logic tests (chunking, hashing, file walking, ask_notes' provider-chain and degradation paths) always run β€” no model, no network, no API key. Tests that need the real fastembed model or the sqlite-vec extension skip honestly when those can't be loaded β€” an offline runner, a blocked model download β€” rather than faking a green result.

What that means in practice, reported exactly as measured:

Environment

Result

βœ… Development environment (fastembed model downloadable)

64 tests, including the real end-to-end flow β€” the fastembed model really loaded, the sqlite-vec extension really ran, and a "how do I bake a cake" query really retrieved the relevant file while excluding the irrelevant one.

⚠️ A sandbox with the model download blocked

50 passed, 14 skipped β€” measured 15 September 2026. Every model-free test green, and the model-backed ones skipped with an explicit reason instead of a false pass.

The second row is the honest cost of the first: this suite tells you when it couldn't verify something.


⚠️ Known limitations

Written down on purpose, because a README that claims no weaknesses is a README you shouldn't trust.

  • No query-instruction prefix is needed anymore. The previous English-only bge-small-en-v1.5 recommended embedding queries with an instruction prefix, which this tool skipped as a v1 simplification. The current default, paraphrase-multilingual-MiniLM-L12-v2, is a symmetric model: queries and passages are meant to embed identically, so the simplification is now simply the correct usage. If you override LOCAL_NOTES_SEARCH_MODEL with an asymmetric model (BGE/E5 family), know that its prefix convention is still not applied.

  • CI re-downloads the fastembed model on every run (no actions/cache configured). Acceptable for a small project; easy to speed up later. Low priority, and honestly labelled as not done.

  • Single-writer SQLite. Concurrent index_directory / search_notes calls from separate processes can collide on writes. The tool is designed around a single MCP client session.


πŸ“œ License

MIT β€” and every runtime dependency was license-checked: sqlite-vec (Apache-2.0), fastembed (Apache-2.0), mcp (MIT), litellm (MIT). No non-commercial or field-restricted weights anywhere in the stack.

Built as part of an ecosystem of small, focused, self-hostable AI tools.

Available Tools

5 tools
ask_notesA

Ask a question in natural language about your indexed files. Retrieves the most relevant chunks (same retrieval as search_notes) and asks an LLM (Groq, then Mistral fallback - needs GROQ_API_KEY or MISTRAL_API_KEY) to synthesize an answer grounded ONLY in those chunks, with file:line sources. Without either key configured, degrades to returning the raw retrieved chunks with a note that no LLM is available - never fails outright just because synthesis isn't possible.

ParametersJSON Schema
NameRequiredDescriptionDefault
top_kNo
questionYes
path_prefixNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.1/5.0
Behavior5/5

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

With no annotations, the description carries full behavioral burden. It discloses LLM provider fallback chain (Groq then Mistral), API key requirements, graceful degradation without keys, grounding in retrieved chunks with file:line sources, and guarantees no outright failure. This is exceptionally transparent.

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?

Though lengthy, the description is information-dense and front-loaded with the core purpose. The second sentence packs several critical behavioral details efficiently. Slightly more compact writing might be possible, but the content justifies the length.

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

Completeness4/5

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

Given the complexity (LLM synthesis, API keys, fallback, grounding), the description covers the essential behavior well. An output schema exists, so not explaining return values is acceptable. Minor gaps like parameter details are addressed under parameter semantics, so overall completeness is high but not perfect.

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 must explain the parameters. It implicitly covers 'question' via the purpose, but says nothing about 'top_k' or 'path_prefix'. The retrieval similarity to search_notes does not substitute for explaining these parameters.

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

Purpose5/5

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

The description states a specific verb ('Ask a question') and resource ('indexed files'), and explicitly contrasts with search_notes by adding LLM synthesis. It is clear what the tool does and how it differs from its closest sibling.

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

Usage Guidelines4/5

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

It notes the same retrieval as search_notes and explains the synthesized vs. raw output difference, implying when to choose this over search_notes. However, it does not explicitly state 'use this when you need a synthesized answer' or provide exclusion criteria, so it stops short of a 5.

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

index_directoryA

Index (or re-index) a local directory for semantic search. Walks recursively, skips .git/node_modules/.venv/etc and files >2MB, and skips any file whose content is unchanged since the last index (cheap: a whole-file hash check before touching the embedding model). Files that were indexed before but no longer exist under path are removed from the index.

Credential-shaped file names (.env, id_rsa, credentials.json, .netrc,
*.pem, ...) are never indexed. If LOCAL_NOTES_SEARCH_ALLOWED_ROOTS is
set, `path` must resolve inside one of its entries; unset (the default)
means any readable directory is indexable.
ParametersJSON Schema
NameRequiredDescriptionDefault
pathYes
extensionsNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.3/5.0
Behavior5/5

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

With no annotations to lean on, the description fully covers side effects and behaviors: recursive walking, skipped directories and size limits, unchanged-file hashing, stale-entry removal, credential-shaped file exclusions, and the LOCAL_NOTES_SEARCH_ALLOWED_ROOTS constraint. This is exemplary disclosure for a tool with no annotations.

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 dense but every sentence earns its place. It is front-loaded with the core purpose, followed by skip rules, side effects, security exclusions, and configuration constraints. There is no fluff or redundant restating of the tool name.

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

Completeness4/5

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

The description is unusually complete for a complex tool: it covers scope, side effects, security, and environment constraints, and an output schema exists to document return values. The only notable gap is the undocumented `extensions` parameter, which prevents a perfect score.

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 must compensate. It adds useful meaning for `path` (must resolve inside allowed roots, must be readable), but it never mentions the `extensions` parameter at all, leaving its purpose and possible values entirely undocumented.

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

Purpose5/5

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

The description opens with a specific verb and resource: 'Index (or re-index) a local directory for semantic search.' It then adds behavioral specifics that distinguish this tool from sibling tools like search_notes and list_indexed_files, making the tool's role unmistakable.

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

Usage Guidelines4/5

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

The description makes the tool's use case clear: build or refresh a semantic-search index for a local directory. It does not explicitly name alternatives or say 'use search_notes instead for queries,' but the context is unambiguous and no exclusion conditions are omitted.

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

list_indexed_filesA

Lists what's currently in the index - file path, chunk count, last indexed time. Useful to check what's covered before searching, or to debug a stale/missing result.

ParametersJSON Schema
NameRequiredDescriptionDefault
path_prefixNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.1/5.0
Behavior4/5

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

No annotations exist, so the description must carry the behavior. It conveys a read-only metadata snapshot ('currently'), states the returned fields, and frames staleness as an expected condition. It does not explicitly state that it never triggers indexing or how recently the index was refreshed, so it is not fully exhaustive.

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?

Two short sentences: the first front-loads the operation and result fields, the second adds pragmatic use cases. No filler or redundancy.

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

Completeness4/5

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

For a read-only listing with one optional parameter and an output schema, the description nearly suffices; the only real gap is the unmentioned path_prefix filtering, but the optional nature and param name mitigate the risk.

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 coverage is 0%, and the description never mentions path_prefix or how filtering works. An agent must infer the parameter's meaning from its title alone, so the description fails to compensate for the missing schema documentation.

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

Purpose5/5

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

Names a specific verb ('Lists') and resource ('what's currently in the index'), and enumerates the returned fields (file path, chunk count, last indexed time). This is enough to separate it from siblings like index_directory or search_notes.

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

Usage Guidelines4/5

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

Gives concrete use cases: checking coverage before searching and debugging stale/missing results. It does not name sibling tools or explicitly list exclusions, but the guidance clearly implies reading index state rather than changing it.

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

remove_directoryA

Removes every indexed file/chunk under path from the index. The index is persistent local state in ~/.local-notes-search/ (or LOCAL_NOTES_SEARCH_DB) - this is how you clean it up without deleting the whole database file.

ParametersJSON Schema
NameRequiredDescriptionDefault
pathYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.4/5.0
Behavior4/5

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

With no annotations, the description carries the full burden of behavioral disclosure. It explicitly describes the destructive scope ('every indexed file/chunk under path'), the persistent local state affected, and that the database file itself is not removed. It could mention irreversibility or permissions, but the core behavior is transparent.

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?

Two compact sentences front-load the action and scope, then add the relevant storage and cleanup context. There is no filler, and every sentence contributes useful information.

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

Completeness4/5

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

For a one-parameter destructive index tool, the description covers what is removed, where the state lives, and why you would use it. The output schema handles return details, so the only mild gaps are explicit caveats about reversibility and path format, which are not critical.

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?

At 0% schema coverage, the description must compensate for the bare 'path' property; it does so by explaining that path defines the subtree whose indexed entries are removed. It does not specify whether path must be absolute or can point to a single file, but the parameter's role is clear.

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

Purpose5/5

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

The description uses a specific verb ('Removes') and names the exact resource ('every indexed file/chunk under path from the index'). This clearly separates it from sibling tools like index_directory or search_notes. It also identifies the storage location, reinforcing that it is an index-cleanup operation.

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

Usage Guidelines4/5

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

The description states this is how you clean up the persistent local index without deleting the whole database file, giving a clear when-to-use context. It does not explicitly name alternative sibling tools, but the cleanup intent is evident and not misleading.

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

search_notesA

Semantic search across everything indexed so far. Returns the top matching chunks with file path, line range, and a relevance-ordered snippet - not just a bag of file names.

ParametersJSON Schema
NameRequiredDescriptionDefault
queryYes
top_kNo
path_prefixNo

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A3.8/5.0
Behavior4/5

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

There are no annotations to lean on, so the description carries the transparency burden. It discloses important behavior: results are relevance-ordered, returned as chunks with file path and line range, and not merely a list of file names. It does not mention side effects, but 'search' strongly implies read-only, and no contradiction exists.

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 two tight sentences, with the verb and scope front-loaded and the return behavior stated in a single clause. The extra 'not just a bag of file names' earns its place by highlighting the key differentiator.

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

Completeness3/5

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

The core purpose and return shape are present, and an output schema exists for detailed return fields, so this is viable. However, there is no guidance on path_prefix semantics or when to prefer ask_notes, and with 0% schema descriptions the agent must infer too much for a tool with three parameters.

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 does not compensate by explaining query, top_k, or path_prefix. 'Top matching chunks' implies the role of top_k and query, but path_prefix is undocumented, and the phrase 'across everything indexed so far' obscures the fact that path_prefix can scope the search.

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

Purpose5/5

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

States a specific verb ('semantic search') over a defined corpus ('everything indexed so far') and goes on to describe the return granularity (matching chunks with file path, line range, and snippet). This distinguishes it clearly from the sibling tools index_directory, list_indexed_files, ask_notes, and remove_directory.

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 description implies use when the agent needs semantic retrieval of relevant chunks, rather than file listing or Q&A, but it never explicitly says when to choose this over ask_notes or how path_prefix narrows the search. Usage context is clear, but exclusions and alternative routing are left to inference.

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. 5 tool updatesv0.1.0
    • First observedask_notes
    • First observedindex_directory
    • First observedlist_indexed_files
    • First observedremove_directory
    • First observedsearch_notes

TDQS

A4.3/5.0

Scored across 5 tools

Disambiguation5/5

Each tool has a clearly distinct role: index_directory ingests data, search_notes and ask_notes are differentiated as raw retrieval vs. LLM-synthesized answers, list_indexed_files provides observability, and remove_directory cleans up. Even the two retrieval-based tools are easy to tell apart because their outputs and purposes are explicit.

Naming Consistency5/5

All tool names follow a consistent snake_case verb_noun pattern: index_directory, search_notes, list_indexed_files, ask_notes, remove_directory. There are no mixed conventions or vague generic verbs.

Tool Count5/5

Five tools is a well-scoped surface for a local notes search server: ingest, search, ask, list, and remove each earn their place. The count is neither bloated nor too thin.

Completeness5/5

The index lifecycle is fully covered: create/update via index_directory, read via search_notes and ask_notes, inspection via list_indexed_files, and deletion via remove_directory. Re-indexing handles changed and removed files, so there are no obvious dead ends or missing operations.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    D
    maintenance
    Local MCP server that provides semantic search (RAG) over code repositories, enabling AI clients like Claude and Gemini to access project context without manual re-upload.
    -
  • F
    license
    Not graded
    quality
    C
    maintenance
    A semantic code search MCP server that enables natural language queries against your codebase, supporting features like related file discovery and context expansion, all running locally.
    2
    -
  • A
    license
    A
    quality
    A
    maintenance
    Local MCP server for semantic code search using Tree-sitter AST parsing, local embeddings, and hybrid search; enables indexing and querying codebases entirely offline.
    5
    MIT