GrooveSeek

Semantic search over a Markdown knowledge base, served over MCP.

View the Project on GitHub alphabet-h/grooveseek

MCP tools, prompts, and resources

The MCP surface GrooveSeek exposes to a connected client.

日本語版: mcp-tools.ja.md

Tools

Tool Description Key parameters
search Hybrid search (vector + FTS5 full-text) merged via Reciprocal Rank Fusion, optionally followed by cross-encoder reranking, optional MMR diversity re-rank, and optional parent retriever content expansion. Returns a wrapper { results, low_confidence, filter_applied } with chunks ranked by relevance; each result may carry expanded_from if parent retriever fired, and — when the chunk came from a source file (v1.2.0+) — start_line / end_line (1-based, describing the chunk) and symbol_kind (the grammar’s word for the definition). Those three keys are absent, not null, on anything else. See docs/citations.md, docs/filters.md, docs/retrieval-pipeline.md. query (required; a whitespace-delimited group led by -, e.g. -async, excludes it from both halves of the search instead of searching for it — v1.1.0+, see retrieval-pipeline.md — and the same three-character trigram floor applies, so -ab excludes nothing), limit, category, topic, rerank (override server default), min_quality, include_low_quality, path_globs (glob list, !-prefix excludes), tags_any / tags_all, date_from / date_to (YYYY-MM-DD), fields / fields_not (object: key → list of strings, only keys the schema declared, v1.9.0+), min_confidence_ratio, mmr / mmr_lambda / mmr_same_doc_penalty (v0.7.0+), parent_retriever (v0.7.0+)
list_topics List all indexed topics and categories with document counts. Call it to learn which category and topic values the search filters accept. (v1.1.0+) Each entry also carries children: the directory tree beneath that category/topic, one node per path segment below the topic, each with segment, file_count (documents under that prefix, so a parent counts everything beneath it) and its own children; root / category-only entries, and topics whose documents sit directly in the topic directory, return []. The tree comes from the stored path, so a document whose frontmatter topic: overrides the path-derived topic still contributes the directories after its second path segment — and because those segments come from that document’s own path, they need not exist as directories under <category>/<topic>/, so a client must not build a path prefix out of them. See MCP surface stability. (none)
get_document Get the full content and metadata of a document by its relative path. The path has to be the document’s one spelling — relative to the knowledge base, /-separated, exactly as search returns it. Any other route to the same file (./a.md, a//b.md, a/../a.md, an absolute path, a different case on Windows or macOS, a backslash or an 8.3 short name on Windows, a symlinked directory) is answered “not found”, so a gateway that filters on the requested string cannot be walked around. A path that is not a name the index can hold — absolute, empty, holding a .., . or empty segment (./a.md, a//b.md), and on Windows a drive, a UNC share, a backslash, a colon anywhere (an alternate data stream such as note.md:secret), a reserved device name such as NUL or COM1, a segment ending in a dot or a space, or one of < > " \| ? * or a control character — is refused before anything on disk is looked at: the answer is the same whether or not something is there, and on Windows no share is contacted. Another spelling that is a name the index could hold (a different case, an 8.3 short name, a symlinked directory) is looked at first, inside the knowledge base only, and then answered “not found” the same way. path (e.g. "deep-dive/mcp/overview.md")
get_best_practice Opt-in: when [best_practice].path_templates is configured in groove.toml, fetch a best-practices document for the given target and optionally extract an h2 section. Without configuration the tool returns a “not configured” error. target (e.g. "claude-code"), category (optional)
rebuild_index Rebuild the search index by scanning all source files (Markdown plus any other extensions enabled via [parsers].enabled). One at a time (v1.0.0+): a call arriving while a rebuild is running is refused with an error naming how long the running one has been going, because a rebuild holds the embedder and the database and search is unavailable until it finishes. The bound is on this tool — groove index runs in its own process and is not affected. Returns the same counters as the CLI summary, including frontmatter_unparsed (v1.7.0+); under [index].fail_on_frontmatter_error = true the reply carries an error key beside those counters when it is non-zero. With an OpenAI-compatible embedding provider, a file whose input the endpoint refuses (HTTP 400 / 413 / 422) is skipped and counted in skipped; a run that had such a refusal before any batch of the file was accepted, and in which the endpoint accepted no batch at all, carries an error key beside the counters, and so does a force: true run with any such refusal, since the rebuild emptied the index first and the file is not in it (ADR-0025). With force: true and an OpenAI-compatible embedding provider, one fixed probe text is embedded before the index is emptied; if the endpoint refuses it the reply is an error saying nothing was removed from the index (ADR-0024). force (optional, default false)
get_connection_graph BFS-expand semantically related chunks starting from a document path. Use it after search when the answer may sit in documents the query’s wording did not reach: the walk follows embedding similarity from the start document, not the query. Returns a flat list of nodes with parent_id / depth / score / snippet so the caller can chain context discovery, plus truncated / truncation[] when a bound cut the walk short. A snippet is an excerpt; call get_document for a node’s full text. start (required, the path to walk from — groove graph --start), depth (default 2, max 3), fan_out (default 5, max 20), min_similarity (default 0.3), seed_strategy (all_chunks / centroid; all-chunks is accepted too), dedup_by_path, category, topic, exclude_paths, max_nodes (default 100, max 2000), max_seed_chunks (default 32, max 1000)

Prompts

(v0.22.0+) Four prompts ship with the server. A client surfaces them as commands the user picks — Claude Code renders them as /mcp__<server>__<name>, where <server> is the key you gave this server in your .mcp.json, not a name GrooveSeek chooses. Each one exists because the tools alone do not say how to combine them: get_connection_graph says it can follow a search, but no tool says how far to expand, when to search again with the vocabulary that turns up, or that a low_confidence flag means the answer should say so.

Prompt Arguments What it asks for
summarize_topic topic (required) Confirm the topic exists with list_topics, gather it with search, read the documents that carry weight with get_document, then summarize — including what the knowledge base does not cover.
deep_dive question (required) Do not answer from the first search: expand the strongest hits with get_connection_graph at depth 2, read whole documents, and search again with the vocabulary that turns up.
whats_new since (optional YYYY-MM-DD; defaults to 30 days ago) Survey documents dated since then. The prompt says outright that date_from filters the frontmatter date — what an author typed — not when a file changed, so this is an approximation and should be presented as one. It also warns that date_from is compared as a plain string: a value that is not YYYY-MM-DD filters out every document rather than erroring.
find_gaps topic (optional) Look for what is missing: questions that come back with low_confidence, and stubs that only appear with include_low_quality: true. Reports absences, does not propose content.

All four are plain text and share one set of citation rules: cite the path of every document used, surface low_confidence rather than answering through it, and say when the knowledge base is silent instead of filling the gap from general knowledge.

They are fixed at compile time rather than configurable. Prompt text goes to the model, and groove.toml is discovered — from the working directory or a .git ancestor — so a configurable prompt set would need the same restriction that already applies to kb_path under an untrusted config. The MCP specification offers no help here: unlike tool annotations, it gives clients no guidance to distrust prompt content.

Resources

(v0.22.0+) The knowledge base is also exposed as MCP resources under the kb:// scheme. In Claude Code these appear in the @ menu.

URI What it is
kb://topic/<prefix> A topic group — the first one or two path segments, the same derivation the indexer uses for category and topic. Reading one returns a Markdown list of the documents under it, with their URIs. kb://topic/ is the root group.
kb://doc/<path> One indexed document, by its knowledge-base-relative path. Advertised as a template rather than enumerated.

resources/list returns the topic groups, not one entry per document. A knowledge base has hundreds of documents but tens of groups, and a listing is something the client fetches on every connect. Individual documents stay reachable through the template and through the uri field that now appears on search hits — the specification permits handing back links to documents a listing never enumerated. Both the listing and that field come from one predicate, so they always agree about a document. Three things take a document off offer while leaving it indexed and findable (on Windows a fourth, a name Windows cannot open as written; see ADR-0023). The first is the active parser registry: if you narrow [parsers].enabled without reindexing, the rows for the dropped extensions stay in the index and stay in search results, but they are no longer offered, because a read would refuse them. The second is size (v0.23.0+): a Markdown or text document over 1 MiB is more than resources/read will return, so it is not offered either — its search hit stays, without a uri. A PDF or spreadsheet over that size is still offered, because a read truncates its extracted text rather than refusing it. A binary document indexed past 50 MiB — possible only when [index].max_binary_file_size raises the index cap — is not offered, because a read refuses a binary file over 50 MiB whatever the index settings (ADR-0026). The third is the decompression cap: a binary document is offered only while [index].max_decompressed_size is at or below the built-in 50 MiB decompression budget: above it, binary hits carry no uri and resources/list and resources/read do not offer them, because a read parses with the built-in budget and the recorded raw size cannot say which documents it would accept. Text documents are not affected. Sizes are recorded during indexing; documents indexed by an earlier version have no size recorded and stay on offer until the next groove index, which fills them in without re-embedding. groove doctor names the documents held back for each of the three reasons (extension-not-registered, larger-than-a-read-returns, binary-uris-withheld) and counts the ones whose size is not recorded yet. Reasoning: ADR-0005.

Separators stay as forward slashes; everything else is percent-encoded, so a path with spaces or non-ASCII characters produces a valid ASCII URI.

A read is bounded by the index. A document is served only if it is indexed, and then only through the same checks get_document applies — symlink and hard-link refusal, path traversal, extension membership, size cap, and a handle-bound read. This is narrower than get_document, which serves anything under kb_path with a registered extension: a resource is something the server offered, so serving a URI that was never on offer is a different operation. .grooveignored documents are therefore absent from resources while remaining readable through get_document, which is ADR-0003’s contract unchanged. The reasoning is in ADR-0004.

Content comes back as text with the media type of what is served: text/markdown for Markdown, text/plain for anything delivered as extracted text. A PDF or a spreadsheet is served as the text groove extracted from it, not as the original bytes.

Not implemented: resources/subscribe and notifications/resources/list_changed. "resources": {} is a conforming declaration without them, and a fixed set of topic groups rarely changes.