mcpField
Z
zoekt-mcp

zoekt-mcp

本地服务
github.com/radiovisual搜索与检索229 周前更新

关于 zoekt-mcp

zoekt-mcp 将 Sourcegraph Zoekt 的索引代码搜索提供给 AI Agent 和 MCP 客户端,适用于需要跨仓库查找代码的开发者。它支持正则表达式和符号查询,并可按仓库、文件或语言缩小范围。

主要功能

  1. 01索引代码搜索
  2. 02正则表达式与符号查询
  3. 03列出可搜索仓库
  4. 04读取指定文件内容
  5. 05按仓库、文件和语言过滤
  6. 06通过 stdio 连接 MCP 客户端

适用场景

  1. 01在多个代码库中定位函数定义
  2. 02查找匹配正则表达式的代码
  3. 03按编程语言缩小搜索范围
  4. 04让编码 Agent 检索已索引代码

MCP 相关

zoekt-mcp 是通过 stdio 运行的 MCP 服务,向客户端提供 search_code、list_repos 和 get_file 工具,并连接到 zoekt-webserver。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"radiovisual-zoekt": {
"command": "uvx",
"args": [
"zoekt-mcp"
],
"env": {
"ZOEKT_URL": "<ZOEKT_URL>"
}
}
}
}
启动命令uvx zoekt-mcp
需要配置的环境变量
ZOEKT_URLBase URL of the zoekt-webserver backend (defaults to http://localhost:6070)

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
pypizoekt-mcp0.3.2
ocighcr.io/radiovisual/zoekt-mcp:0.3.2

工具

还没有获取到工具列表

本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。

详细介绍

zoekt-mcp

CI PyPI ghcr.io MCP Registry License

一个 MCP 服务,将 Sourcegraph Zoekt 代码搜索功能提供给任何兼容 MCP 的 AI Agent——Claude Code、Claude Desktop、Cursor、MCP Inspector 等——让 Agent 能够在你的代码仓库中执行快速、基于索引并支持正则表达式和符号的代码搜索,不受所用编程语言的限制。

  • MCP 服务: 使用 Python 构建,基于 FastMCP,通过 stdio 运行,以便客户端将其作为子进程启动。已发布到 PyPI、 ghcr.io, 并收录于 官方 MCP Registry, 因此无需克隆即可使用。
  • 后端: 你可以通过附在每个 GitHub 发布版本中的 Docker Compose 文件自行运行 zoekt-webserver,也可以将 MCP 服务指向已有的 zoekt-webserver。
  • 提供的工具: search_code、list_repos、get_file。

架构

flowchart LR
    subgraph Client["MCP client"]
        CC["Claude Code<br/>Claude Desktop<br/>Cursor, etc."]
    end

    subgraph Server["zoekt-mcp (Python)"]
        Tools["search_code<br/>list_repos<br/>get_file"]
    end

    subgraph Backend["Docker: zoekt backend"]
        Web["zoekt-webserver"]
        Idx[("zoekt index<br/>named volume")]
        Indexer["zoekt-indexer<br/>(one-shot)"]
    end

    Code[("Your code<br/>bind mount")]

    CC <-->|"stdio<br/>MCP protocol"| Tools
    Tools <-->|"HTTP JSON<br/>/api/search<br/>/api/list<br/>/print"| Web
    Web --> Idx
    Code --> Indexer
    Indexer --> Idx
单次搜索如何在系统中流转
sequenceDiagram
    actor You
    participant Claude as Claude Code
    participant MCP as zoekt-mcp
    participant Web as zoekt-webserver
    participant Idx as zoekt index

    You->>Claude: "where is getVideoId defined?"
    Claude->>MCP: search_code("sym:getVideoId")
    MCP->>Web: POST /api/search
    Web->>Idx: scan shards
    Idx-->>Web: matches + ctags symbols
    Web-->>MCP: raw JSON result
    Note over MCP: trim to {repo, file,<br/>line, text, symbols}
    MCP-->>Claude: shaped result
    Claude-->>You: "src/index.js:17 (function)"

快速开始

从“什么都没安装”到“Claude 可以搜索我的代码”只需三步:安装 MCP 服务、运行后端,并将其接入客户端。这些步骤均无需克隆 Git 仓库。

前提条件

运行 MCP 服务只需满足以下其中一项,后端还需要 Docker:

  • PATH 中有 uv——用于通过 uvx zoekt-mcp 安装。MCP 客户端会通过 uvx 启动服务,因此 which uv 必须能在客户端用于启动进程的 shell 环境中找到可执行文件。每台机器只需安装一次:

    # Official installer (macOS / Linux)
    curl -LsSf https://astral.sh/uv/install.sh | sh
    
    # Homebrew
    brew install uv
    
    # pipx
    pipx install uv
    

    安装程序会将 uv 和 uvx 安装到 ~/.local/bin/(Linux/macOS) 或 %USERPROFILE%\.local\bin\(Windows)中。使用 uv --version 验证。

  • ……或 Docker——用于通过 docker run ghcr.io/radiovisual/zoekt-mcp 安装。任何较新的 Docker Desktop 或 Docker 引擎均可使用。无论如何,后端都需要 Docker,因此如果你尚未安装 uv,选择此方式可以省去安装步骤。

后端还需要:

  • Docker 和 Compose v2——通过附在每个 发布版本中的 compose 文件运行 zoekt-webserver 和一次性索引器。
1. 启动后端(每台机器启动一次)

zoekt 后端是一个由你自行运行的常规 Docker Compose 堆栈——zoekt-mcp 不会管理其生命周期。从最新的 GitHub 发行版获取 compose 文件和辅助脚本,然后针对存放代码的目录启动它们:

# Fetch the two files you need from the latest release.
mkdir -p ~/.zoekt-mcp && cd ~/.zoekt-mcp
curl -LO https://github.com/radiovisual/zoekt-mcp/releases/latest/download/docker-compose.yml
curl -LO https://github.com/radiovisual/zoekt-mcp/releases/latest/download/index.sh
chmod +x index.sh

# Point the indexer at any parent directory on your machine.
# Every top-level subdirectory becomes one searchable repo.
echo "ZOEKT_REPOS_DIR=/home/you/code" > .env

# Bring up zoekt-webserver + the one-shot indexer.
docker compose up -d

例如,如果 /home/you/code 目录结构如下:

~/code/
├── project-a/       → indexed as zoekt repo "project-a"
├── project-b/       → indexed as zoekt repo "project-b"
└── scratch-notes/   → indexed as zoekt repo "scratch-notes"

……zoekt 会在一次操作中为全部三个仓库建立索引,你可以用 repo:project-a 限定查询范围——也可以省略 repo:,一次搜索所有内容。有关一台服务处理多个仓库的模式,请参阅下文的为多个代码库建立索引。

检查是否正常:

curl -s -XPOST -d '{"Q":"repo:."}' http://localhost:6070/api/list \
  | python3 -m json.tool | head -20

你应该会看到 ZOEKT_REPOS_DIR 中的每个子目录都列为一个 zoekt 仓库。

在 macOS / Windows Docker Desktop 上,你选择的路径必须位于允许文件共享的根目录下(检查 Docker Desktop → Settings → Resources → File Sharing)。Linux 没有此限制。

只想试用一下,不想动真实代码目录? 克隆仓库并使用仓库内的测试夹具: ./tests/fixtures/up.sh——请参阅本文末尾的开发。

2. 将 MCP 服务接入客户端

有两种安装方式——选择与你现有工具链相匹配的一种。两种方式最终都会运行同一个带版本号的服务二进制文件;区别仅在于启动方式。

方式 A——uvx(如果你已经安装 uv,推荐使用)

uvx 首次调用时会从 PyPI 下载最新的 zoekt-mcp,将其缓存并启动。无需永久安装,也无需管理虚拟环境。

Claude Code(~/.claude.json):

{
  "mcpServers": {
    "zoekt": {
      "type": "stdio",
      "command": "uvx",
      "args": ["zoekt-mcp"],
      "env": { "ZOEKT_URL": "http://localhost:6070" }
    }
  }
}

也可以通过 claude CLI 操作:

claude mcp add zoekt \
    --env ZOEKT_URL=http://localhost:6070 \
    -- uvx zoekt-mcp

Claude Desktop(macOS 上的 ~/Library/Application Support/Claude/claude_desktop_config.json):

{
  "mcpServers": {
    "zoekt": {
      "command": "uvx",
      "args": ["zoekt-mcp"],
      "env": { "ZOEKT_URL": "http://localhost:6070" }
    }
  }
}

Cursor(~/.cursor/mcp.json 或项目中的 .cursor/mcp.json):

{
  "mcpServers": {
    "zoekt": {
      "command": "uvx",
      "args": ["zoekt-mcp"],
      "env": { "ZOEKT_URL": "http://localhost:6070" }
    }
  }
}

若要固定使用某个版本,而不是始终使用最新版本:

"args": ["zoekt-mcp==0.1.0"]
方式 B——Docker 镜像(无需 Python 工具)

如果你已经为后端运行 Docker,并且不想安装 uv,可以改用容器镜像。MCP 客户端会像使用 uvx 一样,通过标准输入输出启动它。

Claude Code / Claude Desktop / Cursor:

{
  "mcpServers": {
    "zoekt": {
      "command": "docker",
      "args": [
        "run", "-i", "--rm",
        "--network=host",
        "-e", "ZOEKT_URL=http://localhost:6070",
        "ghcr.io/radiovisual/zoekt-mcp:latest"
      ]
    }
  }
}

在 Docker Desktop(macOS/Windows)上,容器内无法通过 localhost 访问主机。请移除 --network=host,改用 host.docker.internal:

"args": [
  "run", "-i", "--rm",
  "-e", "ZOEKT_URL=http://host.docker.internal:6070",
  "ghcr.io/radiovisual/zoekt-mcp:latest"
]

若要固定使用某个版本,请将 :latest 替换为 semver 标签(例如 :0.1.0)。该镜像支持多种架构(linux/amd64 + linux/arm64),因此在 Apple Silicon 和 ARM Linux 主机上无需额外参数即可运行。

3. 重启客户端

重启 Claude Code / Claude Desktop / Cursor 后,应该会显示三个工具(search_code、list_repos、get_file)。试着询问“getVideoId 是在哪里定义的?”,看看它是否会调用 search_code("sym:getVideoId")。

为多个代码库建立索引

一个 zoekt-mcp 服务可以处理任意数量的仓库——这是默认配置。ZOEKT_REPOS_DIR 下的每个顶层子目录都会成为共享索引中的一个独立可搜索仓库。search_code 工具可以使用 repo:NAME(对仓库名称进行正则匹配)限定搜索范围,也可以省略 repo: 来搜索所有内容。

如果你的项目位于不同的父目录下(例如 ~/work/ 和 ~/personal/),最简单的办法是创建一个统一的“索引根目录”,并通过符号链接指向每个项目,然后将 ZOEKT_REPOS_DIR 设置为该索引根目录。一个服务、一份配置,即可搜索所有仓库。

mkdir -p ~/.zoekt-root
ln -s ~/work/project-a       ~/.zoekt-root/project-a
ln -s ~/personal/side-thing  ~/.zoekt-root/side-thing

cd ~/.zoekt-mcp
echo "ZOEKT_REPOS_DIR=$HOME/.zoekt-root" > .env
docker compose up -d

Docker 解析绑定挂载时必须跟随符号链接;这在 Linux 上可行,但在 Docker Desktop 上不一定可靠。如果链接的目录没有出现在容器内,请改为将实际目录(或克隆仓库)放在 ~/.zoekt-root 下,而不要使用符号链接。

何时确实需要两个服务

只有当你需要完全隔离的索引池时,配置第二个 zoekt-mcp 实例才值得投入成本——例如,将工作代码和个人代码放在完全独立的搜索命名空间中,或并行运行两个不同的后端(例如不同版本的 zoekt)。这不是为了索引更多代码所必需的;对于这种需求,一个包含多个子目录的服务才是合适的工具。

如果你确实需要两个实例:

  1. 将 ~/.zoekt-mcp/docker-compose.yml 复制到第二个文件,例如 ~/.zoekt-mcp/docker-compose.personal.yml。

  2. 在副本中修改:

    • compose 项目的 name:(例如 zoekt-mcp-personal)
    • 主机端口映射(例如 6071:6070)
    • 命名卷(例如 zoekt-mcp-personal-index)
    • 容器名称(例如 zoekt-mcp-personal-webserver)
  3. 为第二个堆栈指定独立的环境文件,例如 ~/.zoekt-mcp/.env.personal,并将 ZOEKT_REPOS_DIR 指向 不同的目录。

  4. 使用各自的 compose 文件和环境文件启动每个堆栈:

    cd ~/.zoekt-mcp
    docker compose up -d
    docker compose -f docker-compose.personal.yml \
      --env-file .env.personal up -d
    
  5. 将两个服务分别配置到 MCP 客户端中——使用相同的 zoekt-mcp 二进制文件,但设置不同的 ZOEKT_URL 值:

    {
      "mcpServers": {
        "zoekt-work": {
          "command": "uvx",
          "args": ["zoekt-mcp"],
          "env": { "ZOEKT_URL": "http://localhost:6070" }
        },
        "zoekt-personal": {
          "command": "uvx",
          "args": ["zoekt-mcp"],
          "env": { "ZOEKT_URL": "http://localhost:6071" }
        }
      }
    }
    

Claude Code 会看到两组彼此独立的工具(来自各自命名空间的 search_code / list_repos / get_file),并根据问题决定调用哪一组。

对大多数用户来说,只需一个配置完善的 ZOEKT_REPOS_DIR 服务即可。 除非有明确的隔离理由,否则不要使用多服务方案。

高级用法:在专用的仓库目录下暂存代码

除了将 ZOEKT_REPOS_DIR 指向实际代码目录外,你也可以创建一个专用的暂存目录,并将克隆的仓库或目录放入其中。当你无法向 Docker 暴露实际代码目录时(例如 Docker Desktop 上的公司文件共享限制),或者想临时试用本地没有的仓库时,这种方式很有用:

mkdir -p ~/.zoekt-mcp/repos
git clone https://github.com/myorg/myrepo ~/.zoekt-mcp/repos/myrepo

cd ~/.zoekt-mcp
echo "ZOEKT_REPOS_DIR=$HOME/.zoekt-mcp/repos" > .env
docker compose up -d

这种做法的代价是会带来新鲜度陷阱:现在每个项目都有两份副本——一份是你实际编辑的副本,另一份是暂存副本。重新运行索引器时会重新读取暂存副本,因此每次重新索引前,都需要在 ~/.zoekt-mcp/repos/myrepo/ 中执行 git pull(或用 cp -r 复制你的修改)。除非有明确的理由,否则最好将 ZOEKT_REPOS_DIR 指向你正在使用的代码目录。

工具接口

工具参数返回值
search_codequery: str、max_results: int = 50、context_lines: int = 3{query, file_count, match_count, duration_ns, files: [{repo, file, language, branches, matches: [{line, text, ranges, symbols}]}]}
list_reposfilter: str = ""(可选的 repo: 条件){count, repos: [{name, url, branches, index_time}]}
get_filerepo: str、path: str、branch: str = "HEAD"{repo, path, branch, content}
查询语言

Zoekt 的查询 DSL(完整参考):

条件示例含义
repo:repo:flask-app限定为名称匹配(正则表达式)的仓库
file:file:app.py限定为路径匹配的文件
lang:lang:python限定为某种语言
sym:sym:list_users匹配符号定义
case:yescase:yes Foo区分大小写的内容匹配
/regex//users?/正则表达式内容匹配
(空白)lang:go func main布尔 AND
ordef hello or function hello布尔 OR

保持索引最新

Zoekt 搜索的是预构建索引,而不是直接搜索你的文件。编辑代码后,索引不会自动更新——下一次搜索可能会返回过时的行号、漏掉新添加的符号,或让 Claude 指向已移动或重命名的函数。过时的搜索结果是最浪费令牌的主要原因,因为当 search_code 没有返回有用结果时,Claude 会退而求其次,通过 get_file 读取整个文件。

每次索引器运行时都会发生以下情况:

flowchart LR
    Src["Your code<br/>(live files)"]
    Mount["/src<br/>(read-only<br/>bind mount)"]
    Scratch["/tmp/{repo}<br/>(ephemeral<br/>copy)"]
    Git["throwaway<br/>git repo<br/>+ snapshot commit"]
    Shard[("index shard<br/>/data/*.zoekt")]

    Src -->|bind mount| Mount
    Mount -->|cp -r| Scratch
    Scratch -->|"git init; git add -A;<br/>git commit"| Git
    Git -->|zoekt-git-index| Shard

复制到 /tmp/ 的内容是临时的——每次运行索引器时都会重新复制,绝不会触碰你的真实文件。每次刷新都会读取已挂载源目录中当前的内容。

好在,重新索引很快(即使是大型代码库也只需几秒),完全在 Docker 中运行,不涉及任何大模型调用,也不消耗令牌。你只需要决定要如何触发它。

由于主要快速入门指南已将 ZOEKT_REPOS_DIR 指向你的实时代码目录,每次重新索引都会自动反映最新编辑——无需通过复制来保持同步。(如果你使用的是高级暂存工作流,则需要在触发重新索引前更新 ~/.zoekt-mcp/repos/ 下的克隆,否则 zoekt 只会重新读取过时的副本。)

触发重新索引的方案

以下四种方案都在后台运行——不需要 Claude、不消耗令牌,也不占用上下文窗口。选择最适合你工作方式的方案。

1. 手动重新索引

每当你确认做出了重要更改时,运行 ~/.zoekt-mcp/index.sh。该脚本只会针对当前的 ZOEKT_REPOS_DIR 运行索引器容器,不会重启 Web 服务器,因此搜索始终可用。

~/.zoekt-mcp/index.sh

适用场景: 你只偶尔使用 Claude,不介意开始前输入一条命令。无后台开销。

2. Cron(定时重新索引)

按计划在后台重新索引。无需手动操作,但两次定时任务之间的索引会略微过时。

# Re-index every 15 minutes
*/15 * * * * cd ~/.zoekt-mcp && ./index.sh >/dev/null 2>&1

适用场景: 你大多数日子都会编写代码,希望每次打开 Claude 时搜索结果都基本保持最新。对大多数用户来说,每小时一次就足够了。

3. 文件系统监视器

通过 inotifywait(Linux)或 fswatch(macOS)近乎实时地响应文件更改。有任何编辑都会触发操作,无编辑时则保持空闲。

# Linux: one-liner, run it in a tmux pane or as a systemd --user service
while inotifywait -r -e modify,create,delete,move \
    --exclude '\.git/|node_modules/|__pycache__/' \
    /path/to/your/code 2>/dev/null; do
  ~/.zoekt-mcp/index.sh
done
# macOS equivalent with fswatch (brew install fswatch)
fswatch -o /Users/you/code | xargs -n1 -I{} ~/.zoekt-mcp/index.sh

适用场景: 你希望“搜索始终是最新的,无论何时提问”。注意:对于工具活动频繁的项目(编译器会向构建目录写入文件、IDE 锁文件等),排除列表很重要——如果不设置排除项,就会不断重新索引。

4. Claude Code SessionStart 钩子

每次启动新的 Claude Code 会话时都重新索引,确保每个会话中的首次搜索都是最新的。这可能是大多数用户的最佳默认方案:无需后台进程或 cron 任务,并且索引是否最新恰好与实际使用时机挂钩。

// ~/.claude.json
{
  "hooks": {
    "SessionStart": [
      {
        "command": "$HOME/.zoekt-mcp/index.sh"
      }
    ]
  }
}

适用场景: 你希望没有持续运行的进程,并确保实际使用 Claude 时搜索结果是最新的。会话启动会等待重新索引完成,但最多只需几秒。

我应该选择哪一种?
如果你……使用
……偶尔启动 Claude,不介意手动操作方案 1(手动)
……想要“一劳永逸”,但能接受索引过时 N 分钟方案 2(cron)
……希望搜索始终保持最新,并且可以调整排除列表方案 3(监视器)
……主要通过 Claude Code 会话与代码交互方案 4(SessionStart 钩子)

这些方案并不互斥——例如,同时运行 cron 和 SessionStart 钩子完全没问题,既可以让索引持续保持最新,也能确保会话启动时索引是最新的。

使用 MCP Inspector 手动测试

npx @modelcontextprotocol/inspector uvx --from . zoekt-mcp

Inspector 会在 http://localhost:6274 打开浏览器界面。在 Tools → search_code 下试试:

  • lang:python def hello — 预期在 flask-app/app.py 中找到匹配项
  • lang:javascript USERS — 预期在 express-app/index.js 中找到匹配项
  • sym:users — 预期在两个示例中都找到匹配项

在 Tools → list_repos 下,空筛选条件应返回 flask-app 和 express-app。

开发

本节介绍如何开发 zoekt-mcp 本身。如果你只想使用它,上面的 Quickstart 已涵盖所有内容——无需克隆仓库。只有在你想 修改 Python 服务、运行完整测试套件或发布新版本时,才需要看这里。

设置

克隆仓库,并让 uv 为你管理虚拟环境:

git clone https://github.com/radiovisual/zoekt-mcp
cd zoekt-mcp
uv sync

uv sync 会创建 .venv/,依据 uv.lock 解析所有依赖,并 安装所有运行时和开发依赖。默认情况下会安装开发组(pytest、 pytest-asyncio、respx、ruff、pre-commit、pymarkdownlnt); 如果只需安装运行时依赖,请传入 uv sync --no-dev。

常用开发命令:

uv run pytest                    # run the full test suite
uv run zoekt-mcp --help          # run the CLI from source
uv add <package>                 # add a new runtime dep
uv add --dev <package>           # add a new dev dep
uv lock --upgrade                # refresh uv.lock

若要使用本地克隆运行 zoekt-mcp,并连接到正在运行的后端 (例如迭代修改服务代码时):

uv run zoekt-mcp --zoekt-url http://localhost:6070
发布

发布流程已完全自动化——推送标签会触发流水线,将软件包发布到 PyPI 和 ghcr.io, 并创建附带 compose 文件的 GitHub 发布版本。请参阅 RELEASING.md,了解发布流程(辅助脚本和手动方式),以及首个标签推送前 必须完成的一次性 PyPI/GHCR 设置。

提交流程

代码检查和测试已通过 pre-commit 钩子接入 git 流程,因此你无需记得 手动运行它们。执行 uv sync 后,每个克隆仓库只需安装一次这两类钩子:

uv run pre-commit install --hook-type pre-commit --hook-type pre-push

此后,每次 git commit 都会运行:

  • 对暂存的 Python 文件运行 ruff(ruff check + ruff format --check)——配置位于 pyproject.toml 中的 [tool.ruff]。
  • 对暂存的 Markdown 文件运行 pymarkdownlnt(pymarkdown scan) ——配置位于 pyproject.toml 中的 [tool.pymarkdown]。我们禁用了 MD013(行长度)和 MD046(代码块样式),因为它们会影响易读的正文和 宽表格;同时禁用 MD033,以允许使用用于故障排查的 <details> 块。

每次 git push 也会在推送离开本机前运行离线 pytest 测试套件 (tests/test_client_unit.py 和 tests/test_server_shaping.py),确保损坏的测试永远不会进入远程仓库。 测试被安排在 pre-push 而非 pre-commit 阶段,以保持本地提交的速度; 集成测试不包括在内,因为它需要运行中的 zoekt-webserver。

这些钩子会调用 uv run,因此本地和 CI 中运行的都是 uv.lock 锁定的工具版本, 不同环境之间不会出现版本偏差。同样的代码检查工具和单元测试会在每次推送到 main 以及每个拉取请求时,通过 .github/workflows/ci.yml 运行。

要手动运行所有检查(例如,在创建 PR 前):

uv run pre-commit run --all-files

若要直接修复 Python 格式,而不只是检查格式:

uv run ruff format
uv run ruff check --fix

自动化测试

# Unit tests (no Docker required)
uv run pytest tests/test_client_unit.py tests/test_server_shaping.py -v

# Integration tests: bring the stack up against the examples/ corpus,
# then run the live assertions.
./tests/fixtures/up.sh
uv run pytest tests/test_integration.py -v
./tests/fixtures/down.sh

tests/fixtures/up.sh 会设置 ZOEKT_REPOS_DIR=../examples,并调用相同的 deploy/docker-compose.yml,因此测试夹具不会泄漏到生产部署路径中。集成测试会在 ZOEKT_URL 无法访问时自动跳过,因此即使在没有 Docker 的全新检出中直接运行 uv run pytest,也能通过。

配置

设置环境变量标志默认值
Zoekt 后端 URLZOEKT_URL--zoekt-urlhttp://localhost:6070
HTTP 超时(秒)ZOEKT_TIMEOUT--timeout30

环境变量和标志的作用相同——根据你的 MCP 客户端配置格式选择即可。大多数客户端通过 JSON 配置中的 "env" 块设置环境变量,这就是上面的 uvx 和 Docker 代码片段使用 ZOEKT_URL 而非 --zoekt-url 的原因。

仓库布局

zoekt-mcp/
├── src/zoekt_mcp/         # the Python MCP server
├── tests/
│   ├── test_client_unit.py     # offline unit tests
│   ├── test_integration.py     # live tests (skip when backend down)
│   └── fixtures/               # test-only helpers (up.sh / down.sh)
├── deploy/
│   ├── docker-compose.yml      # generic zoekt backend (env-driven)
│   └── repos/                  # user-populated source mount (gitignored)
└── examples/
    ├── flask-app/              # Flask verification corpus
    └── express-app/            # Express verification corpus

故障排查

常见的索引问题,以问答形式呈现。点击问题可展开答案。

问:search_code 返回 0 条结果,但我确定项目中有这个字符串。怎么回事?

十有八九是索引实际上并未包含你的代码——zoekt 搜索的是另一个(或过期的)语料库。MCP 服务本身不会过滤或改写查询;你发送的内容会原样传给 /api/search,因此 0 条结果就表示索引中没有结果。

分三步诊断:

  1. 让 Agent 调用 list_repos(或 curl -s -XPOST -d '{"Q":"repo:."}' http://localhost:6070/api/list)。这是 zoekt 可见内容的权威来源。

  2. 如果列表中没有你的项目,说明索引器指向了其他位置。常见原因:

    • 缺少 ~/.zoekt-mcp/.env,或其中的 ZOEKT_REPOS_DIR 设置错误,因此 docker compose up 对空目录或非预期目录建立了索引。
    • 有人从开发克隆目录运行了 ./tests/fixtures/up.sh,该脚本会设置 ZOEKT_REPOS_DIR=../examples,并且只为 examples/express-app 和 examples/flask-app 建立索引。
    • 索引器每次运行都会清空 /data/*,因此先前成功建立的索引不会与之后的索引共存——Web 服务器只能看到最近一次运行索引器所建立的索引。
  3. 针对正确的目录重新运行索引器:

    cd ~/.zoekt-mcp
    echo "ZOEKT_REPOS_DIR=/absolute/path/to/parent-of-your-repo" > .env
    docker compose up -d --force-recreate zoekt-indexer
    

    ZOEKT_REPOS_DIR 必须指向一个父目录;其下的每个顶层子目录都会成为一个仓库。索引器退出后,再次运行 list_repos 以确认。

问:索引器退出时显示 WARNING: no repositories were indexed。该怎么办?

ZOEKT_REPOS_DIR 指向的目录中没有可供索引器转换为仓库的顶层子目录。请将 ZOEKT_REPOS_DIR 设置为一个已包含项目子目录的父目录:

cd ~/.zoekt-mcp
echo "ZOEKT_REPOS_DIR=$HOME/code" > .env
docker compose up -d

ZOEKT_REPOS_DIR 顶层的散落文件会被忽略——compose 文件中的循环只遍历目录。

问:list_repos 显示了 express-app 和 flask-app,但没有我的代码。

它们是开发克隆目录中 examples/ 下的仓库内验证测试样例。只要有程序(通常是本地克隆目录中的 tests/fixtures/up.sh)使用 ZOEKT_REPOS_DIR=../examples 运行索引器,它们就会进入你的索引。针对你的实际项目目录重新建立索引(参见上面的第一个问答),这些测试样例就会被替换;索引器每次运行开始时都会清空 /data/,因此无需单独清理。

问:我编辑了文件,但搜索结果仍显示旧内容/行号。

索引是一个快照,并非实时视图。zoekt 只能看到上次运行索引器时 ZOEKT_REPOS_DIR 中的内容。运行 ~/.zoekt-mcp/index.sh 触发刷新,或者按照保持索引最新中的四种自动化方案之一进行设置,让它自动刷新。重新建立索引速度很快(即使是大型仓库也只需几秒),并且完全在 Docker 中运行——不会调用大模型,也不消耗 token。

问:POST /api/search 返回的是 HTML,而不是 JSON。

Web 服务器启动时没有使用 -rpc,因此 /api/* 请求会落入 HTML 搜索处理程序。发行版自带的 docker-compose.yml 已传入 -rpc(参见 zoekt-webserver 下的 command: 区块);如果你在其他位置运行自己的 zoekt-webserver,请在其 argv 中添加 -rpc 并重启。

许可证

MIT——参见 LICENSE。

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。