mcpField
Z
zoekt-mcp

zoekt-mcp

ローカル
github.com/radiovisual検索229 週間前に更新

zoekt-mcp について

zoekt-mcpは、Sourcegraph Zoektのインデックス済みコード検索をAIエージェントやMCPクライアントに提供します。正規表現やシンボルを使った検索に対応し、リポジトリ、ファイル、言語で絞り込めます。

主な機能

  1. 01インデックス済みコードの検索
  2. 02正規表現とシンボルによる検索
  3. 03検索可能なリポジトリの一覧
  4. 04ファイル内容の取得
  5. 05リポジトリ、ファイル、言語で絞り込み
  6. 06stdio経由でMCPクライアントに接続

活用シーン

  1. 01複数リポジトリから関数定義を特定
  2. 02正規表現に一致するコードを検索
  3. 03プログラミング言語で検索対象を絞り込み
  4. 04コーディングエージェントによるインデックス検索

MCP 対応

zoekt-mcpはstdioで動作するMCPサーバーです。zoekt-webserverに接続し、search_code、list_repos、get_fileツールを公開します。

設定

ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの 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

Sourcegraph Zoekt のコード検索機能を、Claude Code、Claude Desktop、Cursor、MCP Inspector など、MCP 対応のあらゆる AI エージェントに提供する MCP サーバーです。作業中の言語に関係なく、エージェントがリポジトリ内で高速なインデックスベースの正規表現/シンボル対応コード検索を実行できます。

  • MCP サーバー: FastMCP を基盤とする Python 製で、stdio 経由で動作するため、クライアントはサブプロセスとして起動できます。PyPI、ghcr.io で公開され、公式 MCP Registry にも掲載されているため、利用時にクローンは不要です。
  • バックエンド: 各 GitHub リリース に添付されている Docker Compose ファイルで、自分で zoekt-webserver を実行します。または、手元にある既存の zoekt-webserver を MCP サーバーの接続先に指定することもできます。
  • 公開するツール: 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
1 回の検索がシステム内をどのように流れるか
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 が自分のコードを検索できる状態」までは 3 つのステップです。MCP サーバーをインストールし、バックエンドを実行して、クライアントに接続します。どのステップでも git clone は不要です。

前提条件

MCP サーバーを実行するために、以下のいずれか 1 つと、バックエンド用の Docker が必要です。

  • PATH 上の uv — uvx zoekt-mcp でインストールする場合に必要です。MCP クライアントは uvx 経由でサーバーを起動するため、クライアントがプロセスを起動するシェルで which uv が解決される必要があります。マシンごとに 1 回インストールします。

    # 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 をインストールしていない場合は、この方法ならインストールせずに済みます。

バックエンドには、以下も必要です。

  • Compose v2 対応の Docker — 各 リリース に添付されている 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 は 3 つすべてのリポジトリを一度にインデックス化します。repo:project-a を指定すれば検索対象を絞り込めます。また、repo: を省略すると、すべてを一度に検索できます。複数リポジトリを 1 つのサーバーで扱う方法については、以下の複数のコードベースのインデックス化を参照してください。

動作確認:

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 サーバーをクライアントに接続する

インストール方法は 2 つあります。既存のツール環境に合う方法を選んでください。どちらも同じバージョン指定のサーバーバイナリを実行します。違いは起動方法だけです。

方法 A — uvx(すでに uv をお使いの場合に推奨)

uvx は初回の実行時に PyPI から最新の zoekt-mcp をダウンロードしてキャッシュし、起動します。恒久的なインストールは不要で、venv の管理も必要ありません。

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 の方法と同様に、stdio 経由でコンテナを起動します。

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 をセマンティックバージョンのタグ(例::0.1.0)に置き換えてください。このイメージはマルチアーキテクチャ(linux/amd64 + linux/arm64)に対応しているため、追加のフラグなしで Apple Silicon および ARM Linux ホスト上で動作します。

3. クライアントを再起動する

Claude Code/Claude Desktop/Cursor を再起動すると、3 つのツール(search_code、list_repos、get_file)が表示されます。「getVideoId はどこで定義されていますか?」のように尋ねて、search_code("sym:getVideoId") が呼び出されることを確認してみてください。

複数のコードベースのインデックス化

1 つの zoekt-mcp サーバーで、必要な数のリポジトリを扱えます。これがデフォルトの動作です。ZOEKT_REPOS_DIR の直下にある各サブディレクトリが、共有インデックス内で個別に検索可能なリポジトリになります。search_code ツールでは、repo:NAME(リポジトリ名に対する正規表現マッチ)を指定して対象を絞り込むか、repo: を省略してすべてを検索できます。

プロジェクトが異なる親ディレクトリ(例:~/work/ と ~/personal/)にある場合は、各プロジェクトへのシンボリックリンクを含む「インデックスルート」ディレクトリを 1 つ作成し、ZOEKT_REPOS_DIR にそのインデックスルートを設定するのが最も簡単です。サーバーは 1 つ、設定も 1 つで、すべてのリポジトリを検索できます。

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 の下に配置してください。

実際に2台のサーバーが必要な場合

2台目の zoekt-mcp インスタンスをセットアップする価値があるのは、インデックスのプールを完全に分離したい場合だけです。たとえば、仕事用のコードと個人用のコードを完全に別の検索名前空間に分ける場合や、異なるバックエンド(例:異なる zoekt バージョン)を並行して稼働させる場合です。コードをもっとインデックス化するだけなら、2台目は必要ありません。その場合は、サブディレクトリを多数持つ1台のサーバーが適切です。

本当に2つのインスタンスが必要な場合:

  1. ~/.zoekt-mcp/docker-compose.yml を2つ目のファイル(例: ~/.zoekt-mcp/docker-compose.personal.yml)にコピーします。

  2. コピーしたファイルで、次の項目を変更します。

    • compose プロジェクトの name:(例:zoekt-mcp-personal)
    • ホストポートのマッピング(例:6071:6070)
    • 名前付きボリューム(例:zoekt-mcp-personal-index)
    • コンテナ名(例:zoekt-mcp-personal-webserver)
  3. 2つ目のスタック専用の環境ファイル(例:~/.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 には、独立した2組のツール(各名前空間の search_code / list_repos / get_file)が表示され、質問に応じてどちらを呼び出すか判断されます。

ほとんどのユーザーには、適切に内容をそろえた ZOEKT_REPOS_DIR を持つ1台のサーバーで十分です。 分離する具体的な理由がない限り、複数サーバーは使わないでください。

上級編:専用の repos ディレクトリにコードを配置する

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

その場合の注意点は、鮮度の落とし穴があることです。すべてのプロジェクトについて、実際に編集するコピーとステージングしたコピーの2つが存在することになります。インデクサーを再実行するとステージングしたコピーが読み込まれるため、インデックスを更新するたびに ~/.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 内で実行され、LLM の呼び出しもなく、トークンも消費しません。あとは、どのように実行するかを決めるだけです。

基本のクイックスタートでは、すでに ZOEKT_REPOS_DIR がコードのあるディレクトリを指すよう設定されています。そのため、インデックスを再構築するたびに最新の編集内容が自動的に反映され、同期のためにコピーする手間はありません。(代わりに高度なステージングワークフローを使っている場合は、再構築を実行する前に ~/.zoekt-mcp/repos/ 内のクローンを更新してください。そうしないと、zoekt は古いコピーを再読み込みします。)

インデックスを再構築する方法

以下の4つの方法は、いずれも別途実行されるため、Claude もトークンもコンテキストウィンドウも関与しません。自分の作業方法に合ったものを選んでください。

1. 手動で再構築する

大きな変更を加えたときに、~/.zoekt-mcp/index.sh を実行します。このスクリプトは、現在の ZOEKT_REPOS_DIR を対象にインデクサーコンテナのみを実行し、ウェブサーバーを再起動しません。そのため、検索は実行中も利用できます。

~/.zoekt-mcp/index.sh

おすすめのケース: Claude をたまにしか使わず、使い始める前にコマンドを1つ実行するのが苦にならない場合です。バックグラウンドでのコストはゼロです。

2. Cron(スケジュールに沿った再構築)

スケジュールに沿ってバックグラウンドでインデックスを再構築します。手動での作業は不要ですが、実行の合間は多少古い状態になります。

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

おすすめのケース: ほとんどの日にコードを編集し、Claude を開くたびに概ね最新の検索結果が欲しい場合です。多くのユーザーにとって、1時間ごとの実行で十分です。

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 でブラウザー UI を開きます。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 自体の開発者向けです。単に 使用するだけであれば、上記のクイックスタートですべて完了します。リポジトリの クローンは不要です。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 リリースを作成します。リリース作成の手順(ヘルパースクリプトと手動の方法)と、 最初のタグを付ける前に必要な PyPI/GHCR の初回セットアップについては、 RELEASING.md を参照してください。

コミットの手順

リンティングとテストは 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(コードブロックのスタイル)を無効にし、 トラブルシューティングの <details> ブロックを使用できるように MD033 も無効にしています。

また、すべての git push では、プッシュがマシンから送信される前にオフラインの pytest スイート (tests/test_client_unit.py と tests/test_server_shaping.py)が実行されるため、 テストが失敗した状態でリモートに送られることはありません。ローカルのコミットをすばやく完了できるよう、 テストは pre-commit ではなく pre-push に設定しています。実行中の zoekt-webserver が必要なため、 統合テストは対象外です。

フックは uv run を呼び出すため、ローカルと CI で実行されるのは uv.lock に固定されたツールのバージョンです。環境間で差異は生じません。同じリンターと単体テストは、 .github/workflows/ci.yml を通じて、 main へのすべてのプッシュとすべてのプルリクエストで実行されます。

すべてを手動で実行するには(たとえば、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

トラブルシューティング

よくあるインデックス作成時の問題を、Q&A形式で紹介します。質問をクリックすると回答が展開されます。

Q: search_code で、プロジェクト内にあると分かっている文字列を検索しても、ヒット件数が 0 件です。何が問題でしょうか?

10 回中 9 回は、インデックスに実際にはコードが含まれていません。zoekt が別の(または古い)コーパスを検索しています。MCP サーバー自体はクエリをフィルタリングしたり書き換えたりしません。送信した内容はそのまま /api/search に渡されるため、ヒット件数が 0 件ということは、インデックス内でヒット件数が 0 件ということです。

次の 3 つの手順で原因を特定してください。

  1. エージェントに 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/* を消去するため、以前の正常な実行結果が後の実行結果と共存することはありません。ウェブサーバーが参照できるのは、最後に実行されたインデクサーの結果のみです。
  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 には親ディレクトリを指定する必要があります。その直下にある各サブディレクトリが、それぞれ 1 つのリポジトリになります。インデクサーの終了後、list_repos を再実行して確認してください。

Q: インデクサーが 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 ファイル内のループはディレクトリのみを処理するためです。

Q: list_repos には express-app と flask-app が表示されますが、自分のコードは表示されません。

開発用クローン内の examples/ にある、リポジトリ内の検証用フィクスチャです。ローカルクローンから tests/fixtures/up.sh を実行した場合などに、ZOEKT_REPOS_DIR=../examples を指定してインデクサーが実行されると、これらがインデックスに含まれます。実際のプロジェクトのディレクトリを対象にインデックスを作り直してください(上記の最初の Q&A を参照)。インデクサーは実行のたびに /data/ を消去するため、これらは置き換えられ、別途クリーンアップする必要はありません。

Q: ファイルを編集しましたが、検索結果には古い内容や行番号が表示されます。

インデックスはスナップショットであり、リアルタイムの表示ではありません。zoekt が参照できるのは、最後にインデクサーを実行した時点で ZOEKT_REPOS_DIR にあった内容だけです。~/.zoekt-mcp/index.sh を実行して更新するか、インデックスを最新の状態に保つにある 4 つの自動化レシピのいずれかを設定して、自動更新されるようにしてください。インデックスの作り直しは大規模なリポジトリでも数秒で完了し、すべて Docker 内で実行されます。LLM の呼び出しは不要で、トークンコストもかかりません。

Q: POST /api/search が JSON ではなく HTML を返します。

ウェブサーバーが -rpc を付けずに起動されたため、/api/* が HTML 検索ハンドラーに渡されています。リリースに同梱されている docker-compose.yml では、すでに -rpc が指定されています(zoekt-webserver の command: ブロックを参照)。別の場所で独自の zoekt-webserver を実行している場合は、起動引数に -rpc を追加して再起動してください。

ライセンス

MIT — LICENSE を参照してください。

コメント

MCP は初めてですか?

プロトコルの概要から最初のサーバーの導入まで、よくある疑問にまとめて答えます。

AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。