你的编码代理在仓库中查找内容时非常擅长,只要这些内容是文本。让 Claude Code 或 Cursor 查询传感器数据表中关于工作温度的说明时,它会使用 grep,却找不到任何内容,随后要么猜测要么放弃,因为数据表是 PDF,决策记录是 Word 文件。

索引 docs 文件夹,然后告诉我传感器数据表中关于工作温度的说明以及哪个 ADR 决定了遥测存储。

带有配置和故障排除的逐步版本在文档中:如何让 Claude Code 或 Cursor 在仓库中搜索 PDF 和 Word 文档.

为什么 grep 返回空结果?

因为 PDF 和 DOCX 并未以纯文本形式存储其单词。DOCX 实际上是一个 zip 压缩包;PDF 则压缩了其文本流。逐字节搜索时,这些单词根本不存在。我们在一个小型仓库中进行了测试:一个 PDF 数据表、一个架构决策记录、一个 DOCX 格式的需求文档,以及一个源代码文件。

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

唯一的匹配是一条指向文档的代码注释,而代理无法读取该文档。这就是差距所在:常量背后的原因、设计背后的决策、测试背后的需求往往正好存在于这些文件中。

建立索引后会有什么变化?

代理获得了文档检索工具。请在仓库根目录注册服务器,使仓库被挂载为存储文件夹:

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

然后相同的问题会得到答案。这是服务器在我们的测试仓库中返回的内容(路径相对于仓库根目录显示):

-> 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…

一次查询同时找到了需求和数据表中的行,这类交叉引用手动操作非常繁琐,且 grep 无法实现。在我们的小型索引中,搜索在首次调用后返回的时间为 80 到 240 毫秒。

代理已经熟悉的循环

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 返回匹配的文件,get_text_fragments 返回带有少量上下文的匹配行,代理会引用两者。这相当于 grep 的功能,但扩展到了 82 种格式:PDF、DOCX、XLSX、PPTX、HTML、Markdown,甚至是有人导入仓库的电子邮件线程。

告诉代理何时使用它

代理会遵循项目指令来决定何时使用工具。在 CLAUDE.md 或 AGENTS.md 中的三行指令可以区分出尝试使用 grep、失败并放弃的代理,以及首先搜索索引的代理:

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.

还有两点注意事项。索引会写入挂载文件夹根目录下的 .groupdocs-search-index/,因此请将其加入 .gitignore。在拉取代码后,update_index 只会重新读取有变动的部分,因此保持索引最新只需一次调用。

Cursor 与 VS Code

相同的容器可在任何 MCP 客户端中使用。在 VS Code 中,工作区的 .vscode/mcp.json 可以挂载当前打开的任意文件夹,从而使索引随项目而动:

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

Cursor 在 .cursor/mcp.json 中的 mcpServers 下使用相同的 command 和 args。将配置文件提交后,项目中的每位开发者都能获得该工具;请将索引本身排除在 git 之外。

为什么不先将所有文件转换为文本?

可以的:让代理在需要时转换每个 PDF,或在仓库中放置 Markdown 副本。对于少量文件这两种方式都可行。索引只会进行一次转换,并通过一次调用保持最新,并在代理读取任何内容之前就告知哪个文件相关,这一步可以让长会话的上下文保持简短。其背后的引擎是 GroupDocs.Search,它直接读取 Office 和 PDF 文档,而无需你维护转换器。

真实的限制

  • 在评估模式下,索引最多容纳 100 个文档,搜索最多返回 10 条结果;请先调用 get_license_status。
  • 全文本检索,而非语义检索:代理必须使用文档中的词汇,或尝试其变体。
  • 不支持 OCR:仅有扫描图像且没有文本层的数据表只能通过文件属性进行索引。
  • 仅限 Docker。在 Linux 上,添加 --user "$(id -u):$(id -g)" 以便容器能够将索引写入你拥有的仓库。

常见问题

Claude Code 能读取我仓库中的 PDF 文件吗?
使用 grep 无法实现。借助 GroupDocs.Search MCP 服务器,它可以在本地搜索并引用 PDF,而无需先进行转换。

它在 Cursor 和 VS Code 中也能工作吗?
可以。 在 .cursor/mcp.json 或 .vscode/mcp.json 中使用相同的 docker run 参数;VS Code 可以挂载 ${workspaceFolder}。

索引会出现在我的提交中吗?
只有在你允许的情况下才会。请将 .groupdocs-search-index/ 加入 .gitignore。

深入了解