Your coding agent is very good at finding things in a repository, as long as they are text. Ask Claude Code or Cursor what the sensor datasheet says about operating temperature and it greps, finds nothing, and either guesses or gives up, because the datasheet is a PDF and the decision record is a Word file. GroupDocs.Search.Mcp runs locally in Docker and gives the agent a full-text index over those documents, with the same search-then-read loop it already uses for code:

Index the docs folder, then tell me what the sensor datasheet says about the operating temperature and which ADR decided the telemetry storage.

The step-by-step version with config and troubleshooting is in the documentation: How to let Claude Code or Cursor search the PDFs and Word documents in a repository.

Why does grep come back empty?

Because PDF and DOCX do not store their words as plain text. A DOCX is a zip archive; a PDF compresses its text streams. Search either one byte by byte and the words are not there. We tested it with a small repository: one PDF datasheet, an architecture decision record and a requirements document in DOCX, and a source file.

$ grep -ri "TimescaleDB\|operating temperature\|I2C" docs/
$ grep -rn "datasheet" src/
src/sensor.py:1:MAX_TEMP_C = 85  # see docs/datasheet

The only hit is a code comment pointing at the document the agent cannot read. That is the gap: the reason behind a constant, the decision behind a design, the requirement behind a test often live in exactly those files.

What changes with an index?

The agent gets a retrieval tool for documents. Register the server from the repository root, so the repository is mounted as the storage folder:

claude mcp add groupdocs-search -- docker run --rm -i \
  -v "$(pwd)":/data ghcr.io/groupdocs-search/search-net-mcp:latest

Then the same questions return answers. This is what the server returned in our test repository (paths shown relative to the repository root):

-> add_to_index {"documentsPath":"docs","indexName":"gateway"}
<- Indexed 'docs' into index 'gateway': 3 document(s) added,
   3 in the index.

-> search {"query":"TimescaleDB","indexName":"gateway"}
<- documentsFound 1: docs/adr-007-telemetry-storage.docx

-> get_text_fragments {"query":"I2C","indexName":"gateway"}
<- === docs/requirements-v2.docx (1 occurrence(s)) ===
     …REQ-15: the I2C bus runs at 400 kHz.…
   === docs/tmp117-sensor-datasheet.pdf (1 occurrence(s)) ===
     …I2C address selection: ADD0 pin to GND gives 0x48…

One query found the requirement and the datasheet line together, which is the kind of cross-reference that is tedious to do by hand and impossible with grep. On our small index, searches came back in 80 to 240 milliseconds after the first call.

The loop the agent already knows

user              agent                         Search MCP server
 |  "What does the datasheet say about I2C addresses?"         |
 |---------------->|                                           |
 |                 |-- search {"query":"I2C"} --------------->|
 |                 |<- requirements-v2.docx,                   |
 |                 |   tmp117-sensor-datasheet.pdf ------------|
 |                 |-- get_text_fragments {"query":"I2C"} --->|
 |                 |<- "...ADD0 pin to GND gives 0x48..." -----|
 |<- "0x48 with ADD0 to GND, 0x49 to V+ (datasheet, p.1);      |
 |    REQ-15 runs the bus at 400 kHz."                         |

search answers which files, get_text_fragments returns the matching lines with a few words of context, and the agent cites both. It is grep’s shape, extended to 82 formats: PDF, DOCX, XLSX, PPTX, HTML, Markdown, even the email threads someone exported into the repo.

Tell the agent when to use it

Agents follow project instructions about when to reach for a tool. Three lines in CLAUDE.md or AGENTS.md make the difference between an agent that tries grep, fails and gives up, and one that searches the index first:

PDF, DOCX and XLSX files under docs/ are indexed in the groupdocs-search MCP index
'docs'. grep cannot read them: use search (quote phrases) and get_text_fragments
for anything in specs, datasheets or ADRs, and cite the file.
Run update_index (indexName: docs) after pulling changes.

Two housekeeping points. The index is written to .groupdocs-search-index/ at the root of the mounted folder, so add that to .gitignore. And after a pull, update_index re-reads only what changed, so keeping the index current costs one call.

Cursor and VS Code

The same container works in any MCP client. In VS Code, a workspace .vscode/mcp.json can mount whatever folder is open, so the index follows the project:

{
  "servers": {
    "groupdocs-search": {
      "type": "stdio",
      "command": "docker",
      "args": [ "run", "--rm", "-i",
                "-v", "${workspaceFolder}:/data",
                "ghcr.io/groupdocs-search/search-net-mcp:latest" ]
    }
  }
}

Cursor uses the same command and args under mcpServers in .cursor/mcp.json. Commit the config file and every developer on the project gets the tool; keep the index itself out of git.

Why not convert everything to text first?

You can: have the agent convert each PDF when it needs it, or check Markdown copies into the repository. Both work for a handful of files. The index does the conversion once, keeps it current with one call, and answers which file matters before the agent reads any, which is the step that keeps a long session’s context small. The engine behind it is GroupDocs.Search, which reads Office and PDF documents directly rather than through a converter you have to maintain.

Honest limits

  • In evaluation mode an index holds at most 100 documents and a search returns at most 10; call get_license_status first.
  • Full-text, not semantic: the agent has to use the words the documents use, or try variants.
  • No OCR: a scanned datasheet without a text layer is indexed by its file properties only.
  • Docker only. On Linux, add --user "$(id -u):$(id -g)" so the container can write the index into a repository you own.

FAQ

Can Claude Code read PDF files in my repository? Not with grep. With the GroupDocs.Search MCP server it can search and quote them, locally, without converting them first.

Does it work in Cursor and VS Code too? Yes. Use the same docker run arguments in .cursor/mcp.json or .vscode/mcp.json; VS Code can mount ${workspaceFolder}.

Will the index end up in my commits? Only if you let it. Add .groupdocs-search-index/ to .gitignore.

Go deeper