Often, no. For a folder of contracts, reports or email, an AI agent with a full-text search tool finds the right files, reads only the matching passages and cites them, and the index runs locally with no embedding model and no vector store. GroupDocs.Search.Mcp gives Claude Desktop, Claude Code, Cursor or Copilot that tool in one Docker command:
Index the contracts folder, then tell me which contracts mention a termination penalty and quote the clause.
The step-by-step version with config and troubleshooting is in the documentation: How to search documents with AI agents using MCP.
Why does everyone reach for embeddings first?
Because “retrieval-augmented generation” arrived as a recipe: split the documents into chunks, embed each chunk, store the vectors, embed the question, take the nearest chunks and paste them into the prompt. It works, and for questions phrased nothing like the documents it is the right tool. But it is a pipeline with an embedding model to call, a store to run, chunking to tune, and a re-embedding job every time the documents change. For “which of my 300 contracts mention a termination penalty”, that machinery answers a question a search index answers directly.
The evidence has caught up with the intuition. A study published on arXiv in late 2025 (Keyword search is all you need) gave an agent nothing but keyword and regex search over a folder and measured it against a standard RAG pipeline: over 90% of the RAG scores, with no vector database, and better suited to collections that change often. Coding agents had already shown the pattern in practice: they search with grep, refine, and search again.
What does the agent do instead?
Three calls. GroupDocs.Search.Mcp exposes eight tools; the loop needs three of them:
user agent Search MCP server
| "Which contracts mention a termination penalty?" |
|---------------->| |
| |-- add_to_index ------------------------->| reads the
| | {"documentsPath":"contracts", | files once,
| | "indexName":"contracts"} | stores text
| |<- "5 document(s) added, 5 in the index" --|
| |-- search ------------------------------->|
| | {"query":"termination AND penalty"} |
| |<- {"documentsFound":3, "documents":[ ... | which files
| |-- get_text_fragments ------------------->|
| | {"query":"\"termination penalty\""} |
| |<- === contracts/acme-...-agreement.pdf ===| the passages
|<- "Two contracts carry one: Acme (3 monthly fees, 12.3), |
| Globex (50% of the remaining fees). Initech excludes it."|
add_to_index reads the files once and keeps the text in a named index on disk. search returns the matching files as JSON with occurrence counts. get_text_fragments returns each match with a few words of context, so the agent quotes the clause instead of loading the whole contract.
Here is what the server actually returned in our test, against five synthetic contracts:
-> search {"query":"termination AND penalty","indexName":"contracts"}
<- documentsFound 3: acme-master-services-agreement.pdf,
globex-consulting-agreement.docx, initech-nda.docx
-> get_text_fragments {"query":"\"termination penalty\"",...}
<- === contracts/acme-master-services-agreement.pdf (1 occurrence(s)) ===
…a termination penalty of three monthly fees applies, payable
within 30…
=== contracts/globex-consulting-agreement.docx (1 occurrence(s)) ===
…Early termination penalty: the Client pays 50 percent of the fees…
Notice the third contract. The Initech NDA matched termination AND penalty because it says “there is no fee and no penalty associated with termination”. The passage is what tells a match from a mention, and an agent that reads the passages says so. That is grounding: every claim in the answer points at a file and a sentence you can open.
Where does each approach win?
Embedding pipeline Agentic full-text search
------------------------------------- -------------------------------------
split, embed, store in a vector store add_to_index: a local full-text index
embed the question, take top-k chunks agent searches, refines, searches again
paste the chunks into the prompt get_text_fragments: quoted passages
re-embed when documents change update_index re-reads what changed
needs an embedding model and a store no model calls, no network at runtime
matches meaning; ranking is opaque matches words; every hit is explainable
Full-text search loses when the question and the document share no words: “which contracts are risky for us?” has no keyword. It wins when the vocabulary is shared, which describes most questions people ask about their own documents: a clause name, a product, a customer, an invoice number, a date range. When a query misses, the agent tries renewal OR renews, or a fuzzy match, the way a person would. The server helps: an unquoted two-word query matches nothing in GroupDocs.Search, and the empty result carries a hint that tells the agent to quote the phrase.
Which tool for which question?
| The user asks | The agent calls |
|---|---|
| “Index this folder” | add_to_index |
| “Which files mention X?” | search |
| “What does it say about X?” | get_text_fragments |
| “Files changed, refresh” | update_index |
| “Forget the drafts folder” | remove_from_index |
| “What is in the index?” | get_index_info |
| “Start a separate index” | create_index |
| “Why only 10 results?” | get_license_status |
The index understands 82 formats, including PDF, Office, OpenDocument, HTML, and email archives (PST, OST, MSG, EML) with their attachments.
How do you run it?
claude mcp add groupdocs-search -- docker run --rm -i \
-v /path/to/documents:/data ghcr.io/groupdocs-search/search-net-mcp:latest
Cursor, Claude Desktop, VS Code with GitHub Copilot and the other clients use the same container in their own config file: Register in AI clients. On Linux add --user "$(id -u):$(id -g)" so the container can write its index into your folder.
Honest limits
- In evaluation mode an index holds at most 100 documents and a search returns at most 10; every response says so. Call
get_license_statusfirst. - It is full-text search, not semantic search: it matches words, not meaning.
- No OCR: scanned PDFs without a text layer are indexed by their file properties only.
- It ships as a Docker image only (the packed tool is over NuGet’s size limit).
FAQ
Can Claude search my local documents without uploading them? Yes. The server runs in a container on your machine and reads the folder you mount; only the passages the agent quotes reach the model.
Is this RAG? It is retrieval-augmented, but agentic: the model decides what to search and when it has enough, using a full-text index instead of embeddings.
What happens when my documents change?
Ask the agent to refresh. update_index re-reads only the changed, added and deleted files.
Go deeper
- Documentation, canonical how-to: How to search documents with AI agents using MCP
- Documentation hub: GroupDocs.Search MCP Server
- For coding agents: grep can’t read PDF: give your coding agent a document index
- Related: Why an AI agent should not read 300 contracts to answer one question
- Related: Grounded answers from your files: quote the passage, not the model’s memory
- Related: Search your email archive with an AI agent: PST, MSG, EML and the attachments inside
- Related: Project memory for AI agents: one index per project, kept current
- On-premise and security model: 3 architectures for AI document processing, and the one that keeps files inside your network
- Questions: GroupDocs Search forum
- Source: GroupDocs.Search.Mcp on GitHub