Code Search MCP
The Code Search MCP server enables AI clients to search and retrieve source code from large, indexed repositories (powered by Zoekt) via two core tools:
search_code: Search across indexed repositories for source code text or regex patterns. Find class/function names, error messages, configuration keys, API calls, etc. Supports:Filtering by repository name (regex), programming language (e.g.,
java,go,python), and file path (regex)Literal search (
literal=true) for strings with spaces, hyphens, or special charactersConfigurable result limit (1–100, default 20)
Returns repository name, file path, line number, and matched snippet
get_file_context: Retrieve surrounding source code for a specific file and line number (from search results). Features:Configurable lines before/after the target (0–100, default 20 each)
Target line highlighted with
>, includes real line numbers, start/end lines, total file lines, and truncation indicator
Safe file access is enforced — only repository-relative paths are accepted; absolute paths and directory traversal (..) are rejected.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Code Search MCP search for @RestController in Java files across all repos"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Code Search MCP
基于 Zoekt 和 Model Context Protocol(MCP)的代码搜索服务,让 AI 客户端能够快速检索大型代码库中的真实代码。
为什么做这个项目
大模型拥有通用编程知识,但默认不了解企业内部代码,也无法知道一个服务当前的目录结构、接口实现、配置依赖和跨仓库调用关系。当研发人员排查问题或理解陌生业务时,仍然需要在多个仓库之间反复切换、搜索和整理上下文。
这个项目尝试解决这一问题:
MCP 连接 AI 与代码库:把代码搜索能力暴露成标准化工具,让支持 MCP 的 AI 客户端可以按需调用,而不是依赖人工复制代码。
Zoekt 提供高性能索引:利用面向源码设计的索引和搜索能力,在大型、多仓库环境中快速定位关键词、符号和代码片段。
为代码理解提供可靠上下文:先检索真实代码,再让模型分析结果,降低仅凭模型已有知识猜测实现的风险。
降低跨团队搜索成本:研发人员不必预先熟悉所有仓库,也可以通过服务名、类名、接口、配置项或错误信息定位相关代码。
沉淀可复用的研发工具:代码检索能力可以继续用于故障排查、代码问答、影响面分析和研发 Agent,而不局限于单一聊天界面。
本项目当前的目标不是让模型一次性“读懂整个代码库”,而是为模型提供一个快速、可控、可追溯的代码检索入口。代码理解、调用链分析和故障定位将在此基础上逐步实现。
Related MCP server: zoekt-mcp
工作方式
用户提出代码问题
│
▼
支持 MCP 的 AI 客户端
│ 调用 search_code
▼
Code Search MCP Server
│ 构造查询并转换结果
▼
Zoekt Web Server
│ 查询预先生成的代码索引
▼
仓库、文件、行号和代码片段职责划分:
src/server.py:声明 MCP 工具,接收工具参数并格式化返回内容。src/services/zoekt_client.py:构造 Zoekt 查询、调用 Zoekt API 并解析响应。src/services/file_reader.py:安全解析仓库内路径并读取目标行上下文。src/models/:定义与 MCP 和 Zoekt 实现解耦的结构化结果模型。
当前能力
项目目前提供 search_code 和 get_file_context 两个工具,支持:
跨已索引仓库搜索代码;
按仓库、语言和文件路径过滤;
普通查询与正则查询;
对包含连字符、空格等特殊字符的内容进行字面量搜索;
返回仓库名、文件路径、行号和匹配代码片段;
限制返回结果数量,避免向模型传入过多上下文;
根据搜索命中的仓库、相对路径和行号读取源码上下文;
拒绝绝对路径和目录穿越,限制文件访问范围。
示例问题:
搜索所有 Java 仓库中使用 @RestController 的代码。
sample-service-v2 在哪些仓库中被引用?
哪个文件读取了指定的配置项?公开 Click 端到端演示
可用公开仓库 pallets/click 8.4.1(固定 commit) 复核一次真实的 MCP 调用闭环。演示以错误文本
The given command does not have a callback that can be invoked. 为查询条件,按
search_code → get_file_context → 仅基于返回源码回答 执行。
重跑的真实返回表明:当 callback 是 Command 且
other_cmd.callback is None 时,Click 在该位置抛出 TypeError。返回范围为第
836~860 行,并带有 truncated=true;因此该证据不把未出现在上下文中的类名或方法名
当作已验证结论。
本地运行
前置条件
Python 3.10 或更高版本;
已运行的 Zoekt Web Server;
已由 Zoekt 建立索引的代码仓库。
安装依赖
项目运行依赖和开发依赖定义在 pyproject.toml 中。创建并激活虚拟环境:
python -m venv .venv
source .venv/bin/activate仅安装运行依赖:
python -m pip install .本地开发或运行测试时,安装可编辑版本和开发依赖:
python -m pip install -e ".[dev]"可选:真实模型决策冒烟
evaluation/run_model_smoke.py 会通过 OpenAI Python SDK 请求一次模型决策,
但不会执行模型返回的检索工具。它要求调用方显式提供
OPENAI_API_KEY、OPENAI_BASE_URL 和 OPENAI_MODEL。建议将本地配置放在已被
Git 忽略的 .secrets/openai-smoke.env,然后执行:
set -a
source .secrets/openai-smoke.env
set +a
PYTHONPATH=. .venv/bin/python evaluation/run_model_smoke.py \
--no-proxy-base-url \
--out .artifacts/model-smoke.jsonOPENAI_BASE_URL 是传输配置:既可以指向 OpenAI API,也可以指向受信任的
OpenAI-compatible API 网关。OpenAIModel 本身只是应用内适配器,并不实现网络代理;
网关需要兼容 Chat Completions 的 strict function tools、tool_choice 和
parallel_tool_calls。远程环境应使用 TLS;仅在隔离且可信的本地/内网链路中接受明文
HTTP,并由部署方承担该链路的安全控制。
若机器设置了通用 HTTP、HTTPS 或 SOCKS 代理,而私有网关必须直连,可显式传入
--no-proxy-base-url。脚本只在当前进程中把 OPENAI_BASE_URL 的主机追加到
NO_PROXY 和 no_proxy,不会输出主机、修改系统代理或影响其他进程。
模型 Trace 只保留模型名、不含传输元数据的 ModelInput、稳定决策/错误分类、关联 ID 和
elapsed_ms。API Key、Base URL、请求头、原始异常和原始 Provider 响应均不进入
Trace。冒烟脚本会在写报告前验证已配置的 Key/Base URL 不在序列化结果中,并输出
redaction_evidence;任一检查失败都会终止执行,报告默认写入被 Git 忽略的
.artifacts/。
该 Click 冒烟会把规范索引名 click 及
Click、pallets/click、github.com/pallets/click 三个精确别名作为
repository_hints 提供给模型,并断言模型最终输出 repo="click"。Agent 执行层还会
通过 RepositoryAliasResolver 做第二次精确解析:原始别名保留在 ModelResult,真正
执行的规范名记录在 ToolCall;未知或冲突别名分别返回 unknown_repository 或
ambiguous_repository,不会再表现为成功的空搜索。
这里的脱敏边界只覆盖 Provider/传输元数据。ModelInput、业务证据和已校验决策会按
设计保留,脚本不是通用源码或 PII 脱敏器;调用方仍需在构造上下文前执行自己的仓库权限
与数据分级策略。
Replay a persisted model decision
无需再次请求模型,即可用公开脱敏 fixture
和单步重放 runner复现一次真实决策的
ToolCall → ToolResult → 下一份 ModelInput。准备好固定 Click 8.4.1 checkout 与 Zoekt
索引后,按真实 Tool 证据中的命令
运行;该入口不读取模型凭据,也不重新生成原始模型 Trace。
Replay one grounded Incident search task
incident-click-search-public.json
冻结一条公开、来源可核验的错误文本及 Click 8.4.1 精确 gold;对应 SHA-256
由同目录 sidecar 和 Runner 常量双重校验。先做不调用模型、Tool 或网络的契约校验:
PYTHONDONTWRITEBYTECODE=1 PYTHONPATH=. .venv/bin/python \
-m evaluation.replay_incident_search \
--fixture evaluation/fixtures/incident-click-search-public.json \
--validate-only真实入口还要求显式配置 ZOEKT_URL、REPOSITORY_ROOT 和
ZOEKT_INDEX_REVISION,并先核对 checkout、zoekt.name、声明 revision 与本地
gold。通过后只把 mapper 产生的唯一 error_text 任务经现有 Tool 执行边界发送给
src.server.search_code 一次;不调用模型、Agent Loop 或 get_file_context,也不重试。
该证据只覆盖直接 Tool backend 路径,不代表 MCP Client 闭环或 Incident 根因判断。
2026-08-29 的首次真实尝试保存在
incident-search-replay-real-2026-08-29.json
及其独立说明。同一冻结
fixture 在零 Tool 预检全部通过后只执行一次,结果稳定分类为
tool_execution_error;预算仍为 1 → 0,其他 Tool、模型和 Agent Loop 调用均为
0,且没有重试。该失败不计为真实检索闭环通过。
2026-08-30 在先确认本地 listener 与固定索引服务已恢复后,新的明确授权允许同一
fixture 再执行一次。零 Tool 预检保持全绿,唯一一次真实 search_code 将冻结 gold
命中 Top-1;预算、Trace、来源和 next State/Input 门禁均通过,仍未调用
get_file_context、模型或 Agent Loop。白名单 artifact 与边界说明见
incident-search-replay-real-2026-08-30.json
和成功复核记录。08-29 的
失败记录未被覆盖,也未通过改 query、fixture、gold 或 best-of 制造成功。
Replay the frozen search result into file context
replay_incident_context.py 严格读取原 fixture
与 08-30 成功搜索 artifact,不重新执行 search_code。先运行纯离线契约校验:
PYTHONDONTWRITEBYTECODE=1 PYTHONPATH=. .venv/bin/python \
-m evaluation.replay_incident_context --validate-only真实运行前还必须显式设置 REPOSITORY_ROOT,并用 --preflight-only 做零 Tool
检查。它核对 Click revision、zoekt.name、目标文件与行、固定 20/20 窗口和上下文
哈希,不调用搜索、模型、Agent Loop 或网络。全部通过后,真实入口只允许一次
get_file_context,结果无论成功或失败都停止且不重试:
PYTHONDONTWRITEBYTECODE=1 PYTHONPATH=. .venv/bin/python \
-m evaluation.replay_incident_context \
--out evaluation/reports/incident-context-replay-real-YYYY-MM-DD.json该 suffix 只证明已验证的 Top-1 搜索证据能确定性驱动一次真实文件上下文读取;它不代表 同一 Trace 内的两 Tool 编排、MCP Client 传输、模型选择、根因质量或完整 Incident Copilot。
2026-09-02 的首次真实 context suffix 已成功:get_file_context=1,
search_code/model/Agent Loop=0,预算 1 → 0,Trace 为
tool_call → tool_result。固定目标行与 1264..1304 上下文通过。白名单结果见
incident-context-replay-real-2026-09-02.json
和独立说明。该 artifact 的
离线完整性门禁后续收紧记录见
09-05 follow-up;修复没有
重跑真实 Tool,也没有修改原 JSON。
离线 Incident analyzer
供应商无关的 IncidentAnalyzer Protocol/Fake 在同一固定 Click 单例上产生非可信
Candidate,analyze_incident() 在来源校验后只调用一次 analyzer,并无条件复用
validate_incident_analysis() 生成可信 Result。输入只含 case ID、已脱敏来源和经
现有来源链验证的单行代码;Fake 脚本、输入快照和返回值彼此隔离。
从项目根目录运行独立演示与反例检查,无需凭证或服务:
PYTHONDONTWRITEBYTECODE=1 PYTHONPATH=. .venv/bin/python \
-m evaluation.validate_incident_analyzer
PYTHONDONTWRITEBYTECODE=1 PYTHONPATH=. .venv/bin/python -m pytest -q -p no:cacheprovider \
test/test_incident_analyzer.py test/test_incident_analysis.pyRunner 从公开入口验证正常/证据不足分支、非法来源、错误类型、伪造 Result、输入
污染、脚本耗尽及快照隔离,并阻断真实模型、Tool、Loop、网络和子进程执行。
输出中 fake_calls 单独计数,包含脚本耗尽的尝试;calls 不含历史 artifact 中的
Tool 调用。结果及验收范围见 09-06 报告
和 固定 JSON。本次只证明
候选生成与可信结构/来源之间的边界,不证明根因质量或置信度校准。
冻结的 Agent Eval
独立 Agent 评测集位于
evaluation/agent_cases.jsonl,固定为 Click 8.4.1
revision 6eeb50e948ea136db145280f6f5dd52eca3fa7e5 上人工复核的 10 条自然语言任务:
8 条可回答、2 条应以信息不足收口。冻结 SHA-256 为
09b3e346686d550c09c3b361ec16d00e51cac8fd257850593901ab212c2da129。
真实运行前先做不调用模型或 Zoekt 的契约校验:
PYTHONDONTWRITEBYTECODE=1 PYTHONPATH=. .venv/bin/python \
evaluation/run_agent_eval.py --validate-only真实入口要求调用方提供 OPENAI_API_KEY、OPENAI_BASE_URL、OPENAI_MODEL,以及由
索引创建方确认的 ZOEKT_INDEX_REVISION;同时要求 ZOEKT_URL、
REPOSITORY_ROOT/click、checkout revision 和 git config zoekt.name 一致。Runner 会在
任何模型调用前执行固定的正向/负向索引探针,并拒绝非默认文件或 SHA-256 不匹配的真实
评测。完整 10 条按文件顺序各运行一次,不提供 --max-cases、重试或 best-of 选项:
set -a
source .secrets/openai-smoke.env
set +a
export ZOEKT_INDEX_REVISION=6eeb50e948ea136db145280f6f5dd52eca3fa7e5
PYTHONDONTWRITEBYTECODE=1 PYTHONPATH=. .venv/bin/python \
evaluation/run_agent_eval.py \
--no-proxy-base-url \
--out evaluation/reports/agent-eval-YYYY-MM-DD.json \
--summary-out evaluation/reports/agent-eval-YYYY-MM-DD.md逐题 JSON 保留终态、Tool 尝试与名称序列、模型提交的引用、当前任务成功 Tool 来源校验、 金标范围匹配、Trace 不变量、模型/Tool/任务耗时和稳定失败分类;汇总指标只从这些明细 重算。写盘前会断言 API Key、Base URL、本机仓库路径和传输/Provider 原始字段均不存在。
2026-08-16 的首轮真实执行保存在
agent-eval-2026-08-16.json 和
agent-eval-2026-08-16.md。该轮 10/10
均在首次模型调用处以 model_execution_error 终止,任务成功率为 0/10,Tool 尝试为
0,引用分母为 0,Trace 完整性为 10/10。
模型服务恢复后,用户明确授权使用相同冻结数据、Prompt、检索和金标执行一次独立恢复
重试;结果保存在
agent-eval-2026-08-16-retry-01.json
和
agent-eval-2026-08-16-retry-01.md。
重试轮任务成功率为 3/10,引用有效率 6/20(30%),Trace 完整性 10/10,每题 Tool
上限全部满足。6 个正向答案因模型提交范围格式引用而 fail closed,另 1 条为 Tool 错误;
没有根据结果修改机制或执行第三轮,M3 退出门槛仍未达到。
这 6 条范围引用失败已按原始报告哈希冻结在
agent-eval-range-citation-failures-2026-08-16.json,
逐条保留模型输出、人工复核的结构化单行参考引用和稳定失败分类。下一项预注册的可证伪
假设见
agent-eval-range-citation-freeze-2026-08-16.md:
仅把最终决策中的自由文本引用改为严格的 repo / path / 正整数 line 字段,当前任务
成功 Tool 事实的精确运行时校验保持不变,不增加范围兼容或自动修复。
独立冻结的 Agent Holdout
未参与上述 Prompt 调试、失败分析或结构化引用实现的新 Click 8.4.1 holdout 位于
evaluation/agent_holdout_cases_2026-08-19.jsonl。
它独立保持 8 条可回答与 2 条应信息不足任务,冻结 SHA-256 为
e08cd744a44b3fb99ac98d0f5bf77ef6489f324c8109aba980687f459a0df396。
agent-holdout-exclusion-set-2026-08-19.json
逐条保留原 10 条 Agent case、18 条检索 case 和 6 条范围引用失败的 ID、问题或查询、目标位置及源文件哈希。完整人工源码复核、两条全仓无命中命令和诚实边界记录在
agent-holdout-freeze-2026-08-19.md。
以下入口只校验 8/2 分布、ID 和问题唯一性、Click revision、数据与排除集哈希、排除集
10/18/6 覆盖,以及正向 gold 与旧目标位置不相交;它不读取模型凭据,不调用模型或
Zoekt:
PYTHONDONTWRITEBYTECODE=1 PYTHONPATH=. .venv/bin/python \
evaluation/run_agent_holdout.py --validate-only后续独立工作日的真实运行也使用 evaluation/run_agent_holdout.py,且只接受默认冻结文件。
默认报告名为 agent-holdout-eval-YYYY-MM-DD.json 和
agent-holdout-eval-YYYY-MM-DD.md,不会覆盖 08-16 的原评测报告。冻结当天没有运行真实
holdout,也没有预填任何新质量指标。
配置 Zoekt 地址
默认连接 http://localhost:6070。如果 Zoekt 运行在其他地址,可以设置:
export ZOEKT_URL=http://localhost:6070
export REPOSITORY_ROOT=/path/to/indexed/repositoriesREPOSITORY_ROOT 下的一级目录名需要与 Zoekt 返回的仓库名一致。例如:
/path/to/indexed/repositories/
├── code-search-mcp/
└── demo-service/此时 Zoekt 命中中的 repo 应分别为 code-search-mcp 或 demo-service,
get_file_context 才能找到对应的本地源码。不要把 API Key、公司内网地址或
本地私有路径提交到仓库。
索引前固定 Zoekt 仓库名
本项目使用 REPOSITORY_ROOT/<repo> 将搜索结果映射到本地源码。对每个待索引的
Git 仓库,必须在生成或重建 Zoekt 索引前进入该仓库并执行:
git config zoekt.name <本地目录名>例如,本地目录为 REPOSITORY_ROOT/click 时:
cd /path/to/indexed/repositories/click
git config zoekt.name click随后再运行 Zoekt 的索引命令。若仓库已经索引过,修改配置后还必须重建该仓库的 索引;已有索引中的仓库名不会自动更新。
启动 MCP Server
PYTHONPATH=. .venv/bin/python -m src.server该服务使用 stdio 与 MCP 客户端通信,因此直接启动后没有普通 HTTP 页面属于正常现象。
配置 MCP 客户端
不同客户端的配置入口不同,核心配置如下。请把 cwd 和环境变量替换为你
自己的绝对路径:
{
"mcpServers": {
"code-search": {
"command": "/absolute/path/to/mcp-server/.venv/bin/python",
"args": ["-m", "src.server"],
"cwd": "/absolute/path/to/mcp-server",
"env": {
"PYTHONPATH": ".",
"ZOEKT_URL": "http://localhost:6070",
"REPOSITORY_ROOT": "/absolute/path/to/repositories"
}
}
}
}使用 MCP Inspector 调试
PYTHONPATH=. .venv/bin/mcp dev src/server.py:mcp该命令会通过 MCP Inspector 加载 FastMCP 服务,可以查看工具 schema 并手动调用
search_code 和 get_file_context。
工具说明
search_code
在 Zoekt 索引中定位精确的源码文本或正则模式。适合查找类名、函数名、错误 信息、配置键和调用表达式;它是文本/正则搜索,不等同于语义级符号分析。
参数 | 类型 | 默认值 | 说明 |
|
| 必填 | 源码文本或 Zoekt 正则表达式 |
|
|
| 仓库名正则过滤 |
|
|
| 语言过滤,如 |
|
|
| 仓库内文件路径正则过滤 |
|
|
| 返回命中数,范围 1~100 |
|
|
| 是否把 |
返回值示例:
{
"query": "UserNotFound",
"duration_ms": 3,
"matches": [
{
"repo": "demo-service",
"path": "src/service/user_service.py",
"line": 128,
"snippet": "raise UserNotFound(user_id)"
}
]
}以下情况建议设置 literal=true:
sample-service-v2
connection refused: upstream unavailable
com.example.user-serviceget_file_context
读取一次搜索命中周围的源码。通常先调用 search_code,再将某条命中的
repo、path 和 line 分别传给 repository、file_path 和
line_number。
参数 | 类型 | 默认值 | 说明 |
|
| 必填 |
|
|
| 必填 |
|
|
| 必填 |
|
|
|
| 目标行之前的行数,范围 0~100 |
|
|
| 目标行之后的行数,范围 0~100 |
返回的 content 带真实行号,并以 > 标出目标行:
126 | user = repository.find(user_id)
127 | if user is None:
> 128 | raise UserNotFound(user_id)
129 | return user为避免任意文件读取,file_path 只接受仓库内相对路径,绝对路径和逃出仓库
根目录的 .. 路径都会被拒绝。
完整调用流程
向 MCP 客户端提问:
在 demo-service 中查找 UserNotFound,读取最相关命中前后各 10 行,
然后说明这段代码在什么情况下抛出异常。若信息不足,请明确说明。理想工具调用链:
search_code(query="UserNotFound", repo="demo-service", limit=10)
↓
选择相关的 repo + path + line
↓
get_file_context(
repository="demo-service",
file_path="src/service/user_service.py",
line_number=128,
lines_before=10,
lines_after=10
)
↓
客户端基于真实源码回答,并引用文件与行号手动验证搜索
确保 Zoekt 已启动并已加载索引,然后执行:
PYTHONPATH=. .venv/bin/python test/test_search.py运行测试
单元测试默认不要求启动真实 Zoekt:
PYTHONPATH=. .venv/bin/pytest -v若环境中没有 pytest,先安装:
pip install pytest真实 Zoekt 验证(公开)
公开版使用 Click 8.4.1 作为真实 MCP Client 验证样例。完整的
search_code → get_file_context 调用轨迹、原始可见返回结果和限制说明见
上方“公开 Click 端到端演示”中的两份证据。
Click 检索评测基线
公开评测集位于 evaluation/cases.jsonl,当前共 18 条,固定使用
pallets/click 8.4.1 commit 6eeb50e948ea136db145280f6f5dd52eca3fa7e5。
先确认本地 checkout 与索引都使用该 revision,且 Zoekt 返回的仓库名为 click:
git -C "$REPOSITORY_ROOT/click" rev-parse HEAD
PYTHONPATH=. .venv/bin/python evaluation/run_eval.py --validate-only
PYTHONPATH=. .venv/bin/python evaluation/run_eval.py \
--out evaluation/reports/baseline-YYYY-MM-DD.json评测集包含正向命中、无命中、上下文边界,以及重复命中、无关结果、未限定
路径、符号候选、错误文本、语言/路径组合和长文件上下文等样例。每个正向样例声明金标
repo、path 与精确行号或可接受行号范围;无命中样例则明确声明受检
repo、path、line: null 和 failure_reason。无命中正确时不会调用
get_file_context,也不会被算入 Hit@1、Hit@5 或 search_code → get_file_context
闭环的分母,而是在 no_match_success 中单独统计。
固定 revision 上的逐条人工 git grep 复核记录见
evaluation/click-8.4.1-manual-grep.md。该清单记录每条样例的类别、目标仓库、路径和可接受行号范围。重复候选、无关结果和未限定路径的样例会保留 Zoekt 原始返回顺序;即使金标未排在 Top-1 或 Top-5,也不会为了提高指标收窄查询或调整排序。
2026-07-23 真实 Zoekt 重跑结果
baseline-2026-07-23.json 与 2026-07-22 的基线对比如下。新集新增的 8 条故意覆盖了候选重复、文档/注释噪声和未限定路径的情况,因此质量指标的分母扩大,不能把百分比降低解读为对原 10 条已覆盖场景的回归。
指标 | 2026-07-22(10 条 / 8 类) | 2026-07-23(18 条 / 15 类) |
正向 / 无命中样例 | 8 / 2 | 16 / 2 |
Hit@1 | 8/8(100%) | 12/16(75%) |
Hit@5 | 8/8(100%) | 13/16(81.25%) |
| 8/8(100%) | 12/16(75%) |
无命中正确率 | 2/2(100%) | 2/2(100%) |
平均 search wall latency | 28.551 ms | 19.231 ms |
平均 closed-loop wall latency | 29.840 ms | 20.320 ms |
重跑没有搜索或上下文读取错误。4 条如实保留为 top1_miss:两个 duplicate_hits、irrelevant_results,以及 unscoped_path;其中 unscoped_path 的金标仍在 Top-5。延迟反映此次本地服务状态,适合作为同一环境内的对照而非跨机器 SLA。
两个 context_boundary_* 样例分别命中 src/click/globals.py 的第 1 行和第
64 行,并断言 get_file_context 返回的实际 start_line、end_line、
total_lines、truncated 以及目标行标记。这会覆盖文件开头和结尾的截断行为。
报告会记录每条样例的原始 wall-clock 时间、聚合的 latency_ms.search_wall 与
latency_ms.closed_loop_wall,以及本地 checkout revision。调用顺序、固定查询和
time.perf_counter 的计时定义也写入 JSON,因而质量指标可以精确复现;延迟会随
机器与 Zoekt 运行状态变化,应比较同一环境下的报告而非要求逐毫秒一致。
本地私有仓库的测试基线和输出只保存在被 Git 忽略的目录,不纳入公开仓库或发行物。
每次已启用的执行都会将可复现性信息写入
.artifacts/zoekt-integration.json(可通过 ZOEKT_INTEGRATION_REPORT 改写路径):
Zoekt 地址、Python/HTTP 客户端信息、Zoekt Server commit、索引仓库 commit、本地
checkout commit、起止时间、耗时和执行结果。当前演示环境的服务器 /about 未暴露
build commit,因此该字段会记录为不可获取;如部署方能确认该值,可额外设置
ZOEKT_SERVER_COMMIT,它会优先写入报告。
常见问题
客户端启动后看不到工具
确认客户端配置中的 Python、
cwd都是绝对路径;确认虚拟环境已经安装
requirements.txt;确认以项目根目录作为工作目录,并设置
PYTHONPATH=.;在 MCP Inspector 中先验证服务能否加载。
search_code 报连接失败
确认 Zoekt Web Server 正在运行;
确认
ZOEKT_URL可从 MCP Server 进程访问;确认 Zoekt 已加载目标仓库索引。
搜索有结果,但 get_file_context 提示仓库或文件不存在
确认
REPOSITORY_ROOT/<repo>是实际存在的目录;确认 Zoekt 返回的仓库名与本地一级目录名一致;
确认搜索结果中的路径是仓库内相对路径;
确认 Zoekt 索引对应的源码与本地源码版本一致。
Zoekt 返回的 repo 与本地目录不一致
公开 Click 演示的首次调用就是这一失败案例:search_code 返回
repo=github.com/pallets/click,而本地源码目录是
REPOSITORY_ROOT/click。get_file_context 按 Tool 返回值拼接本地目录后,会尝试
读取 REPOSITORY_ROOT/github.com/pallets/click,因而报 Repository 不存在。
根因是未显式设置 zoekt.name 时,Zoekt 可能根据 Git origin 推导出
github.com/<owner>/<repo> 形式的仓库名;该名字与本项目按本地一级目录寻址的契约不一致。
修复方式是在索引前进入本地 checkout,执行
git config zoekt.name <本地目录名>,然后重建索引。例如目录名为 click 时执行
git config zoekt.name click。修复后先确认 search_code 返回 repo=click,再将其
返回的 repo、path、line 原样传给 get_file_context。
搜索服务名或完整错误信息时没有结果
带空格、连字符或 Zoekt 查询运算符的完整文本应设置 literal=true。需要正则
能力时则保持 literal=false。
设计取舍
为什么使用 Zoekt,而不是向量数据库?
当前阶段主要解决精确代码检索问题,例如类名、方法名、错误信息、配置项和 API 调用位置。这些查询通常包含明确的文本或正则特征,Zoekt 更适合作为第一层检索基础设施。
向量检索更适合语义相似内容,但可能遗漏必须精确匹配的符号,也会引入代码切分、Embedding 更新和索引一致性等额外成本。后续可以采用“Zoekt 精确检索 + 语义检索”的混合方案,而不是让二者互相替代。
为什么通过 MCP 暴露能力?
MCP 将搜索能力与具体 AI 客户端解耦。同一个服务可以被不同的 Agent 或开发工具调用,也便于未来继续增加 find_symbol 和 find_references 等工具。
Roadmap
接入 Zoekt 搜索 API
提供
search_codeMCP 工具支持仓库、语言、路径和字面量过滤
补充 Zoekt 查询构造和响应解析单元测试
补充 MCP Server 基本测试
补充真实 Zoekt 集成测试(默认关闭)
增加
get_file_context增加
find_symbol和find_references控制上下文长度并改善结果排序
接入代码理解 Agent
结合错误日志实现故障代码定位
当前限制
当前演示环境的 Zoekt Server
/about未暴露 build commit,故 Server commit 不可获取; 不应根据猜测补写版本或 commit。部署方可通过ZOEKT_SERVER_COMMIT显式提供已确认的值。get_file_context只返回目标行前后请求的有限行数(每侧最多 100 行)。当返回truncated=true时,文件前部或后部没有包含在上下文中;未出现在返回内容中的类、方法或 调用关系不能视为已验证。搜索结果质量依赖 Zoekt 索引是否及时、完整;
当前主要提供文本和正则搜索,不等同于完整的语义代码理解;
当前不会自动判断搜索结果中的代码是否正确,也不会直接修改代码;
企业代码接入时还需要补充仓库权限、访问审计和敏感信息保护。
Available Tools
2 toolsget_file_contextA
读取一次代码搜索命中位置周围带行号的源码上下文。
应在 search_code 返回候选命中后调用,并原样传入该命中的 repo、path 和
line。适合确认命中属于定义、调用还是配置,以及在回答用户前理解附近
的控制流。返回内容以 > 标出目标行,并包含实际起止行、文件总行数
和是否截断。不要用它搜索未知文件,也不要传入绝对路径或 .. 路径。
Inputs: file_path (required), repository (required), line_number (required), lines_after, +1 more. Returns: content, end_line, file_path, truncated, +4 more
search_codeA
在 Zoekt 已索引的仓库中定位源码文本及其文件位置。
当用户询问符号定义或引用、错误信息来源、配置项用法、接口实现或精确 代码模式时使用。结果包含仓库、仓库内相对路径、从 1 开始的行号和命中 行片段;需要理解周围实现时,再用该结果调用 get_file_context。
query 默认按 Zoekt 查询语法解释。搜索包含空格、连字符或查询运算符的 完整错误信息、服务名或包名时设置 literal=true。已知仓库、语言或路径 范围时应使用对应过滤器,以减少无关结果。该工具只做文本/正则检索, 不执行语义分析,也不保证命中就是定义。
Inputs: query (required), lang, path, repo, +2 more. Returns: query, matches, duration_ms
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 2 tools
The two tools have clear, non-overlapping purposes: search_code finds code locations based on text queries, while get_file_context retrieves surrounding source context for a specific hit. No ambiguity exists.
Both tool names follow a consistent verb_noun pattern (search_code, get_file_context) using snake_case. The naming is predictable and clearly conveys the action and object.
With only 2 tools, the server is minimal but focused on a narrow domain: code search and context retrieval. While some might expect additional filtering or browsing tools, the count is appropriate for a targeted utility.
The server covers the core workflow of searching code and retrieving file context. Minor gaps exist (e.g., no direct language-specific filter, no batch operations), but the surface is sufficient for the stated purpose.
Maintenance
Related MCP Connectors
Hosted code graph over MCP: exact callers, dependencies, and cross-repo blast radius for AI agents.
Agent-driven search: build, import, tune, search, and score result quality — all over MCP.
Web search API and MCP server for developers building AI agents.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceAn MCP server that provides code search capabilities for AI tools and LLMs using Zoekt, allowing for searching across codebases with advanced query syntax and content fetching.23MIT
- AlicenseAqualityDmaintenanceExposes Sourcegraph Zoekt code search to MCP-capable AI agents, enabling fast, indexed regex/symbol-aware code search over repositories.349 PyPI2MIT
- AlicenseNot gradedqualityCmaintenanceProvides two-modal code search (lexical via Zoekt and semantic via ChromaDB embeddings) for AI coding agents through MCP tools, enabling fast regex and meaning-based code lookup.MIT
- FlicenseNot gradedqualityDmaintenanceExposes Cursor's semantic code search through MCP, enabling tools like codebase_search to query indexed repos.1-