mcpField
I
indexio

indexio

本地服务
ioservices.io开发工具86 天前更新

关于 indexio

indexio 为组织的代码仓库建立索引,支持快速词法、语义和混合搜索、符号查询及调用图分析。它面向需要本地、可迁移代码上下文的开发者和编码 Agent,无需数据库或外部服务。

主要功能

  1. 01词法、语义和混合搜索
  2. 02符号定义与调用图
  3. 03符号及差异影响分析
  4. 04索引仓库和代码目录
  5. 05增量同步

适用场景

  1. 01按标识符或概念查找代码
  2. 02修改符号前追踪调用方
  3. 03为编码 Agent 提供可搜索的仓库上下文

MCP 相关

indexio 以 stdio 方式运行 MCP 服务,提供代码搜索、符号查询、文件读取和影响分析工具,也提供 HTTP API。

配置

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

mcp.json · 通用配置
{
"mcpServers": {
"ioserviceslabs-indexio": {
"command": "npx",
"args": [
"-y",
"indexio"
]
}
}
}
启动命令npx -y indexio

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

安装包
npmindexio0.1.10
pypiindexio-cli0.1.10

工具

13

收录的工具列表 · 更新于 6 天前

参数类型必填说明
modeenum否lexical | semantic | hybrid
limitinteger否最大结果数(默认 50)
linesinteger否lexical:每个文件中匹配行数(默认 1,最多 100)
querystring是搜索查询
fusionenum否仅限 hybrid(默认 rrf)rrf | combmnz
rerankboolean否仅限 hybrid(默认 false);需要配置 rerank 端点,否则无效

详细介绍

indexio

release npm PyPI license

indexio 是一款面向 AI 编码 Agent 的代码搜索引擎。它一次性为组织中的所有代码仓库建立索引,能在毫秒内回答查询。它通过模型上下文协议(MCP)和 HTTP API 将索引作为工具提供给 Agent。

indexio 是一个用 Rust 编写的单一二进制文件。它不需要数据库或外部服务。索引由一个数据目录中的一组文件构成。你可以将该目录复制到另一台机器。

让你的 Agent 安装它

将下面的代码块粘贴到 Claude Code、Cursor、Codex 或任何能运行 shell 命令的编码 Agent 中。它会安装 indexio、为代码建立索引、注册 MCP 服务并验证结果。将 ~/code 替换为存放代码仓库的文件夹。

Install indexio (https://github.com/IOServicesLabs/indexio) on this machine and connect it
to me. Do these steps in order and stop at the first failure with the exact error.

1. Install the binary.
   - Linux or macOS:  curl -fsSL https://raw.githubusercontent.com/IOServicesLabs/indexio/main/install.sh | sh
   - Windows (PowerShell):  irm https://raw.githubusercontent.com/IOServicesLabs/indexio/main/install.ps1 | iex
   - If neither works, use `pip install indexio-cli`, `npm install -g indexio`, or `cargo install --git https://github.com/IOServicesLabs/indexio indexio`.
   The binary lands in ~/.local/bin (Linux, macOS) or %LOCALAPPDATA%\Programs\indexio (Windows).
   If `indexio --version` is not found afterwards, add that folder to PATH and open a new shell.
   `git` must be on PATH.

2. Index my code:  indexio add ~/code
   This registers every git repository under the folder at any depth and builds the index.
   Nothing is uploaded; the index lives in ~/.indexio.

3. Register the MCP server with the agent you are.
   - Claude Code:  indexio setup claude --claude-md ~/.claude/CLAUDE.md
     then  indexio hook install
     (the hooks route file reads and greps through the index; scripts and builds run as before)
   - Any other MCP client: add this server to its MCP configuration and restart the client:
       command: indexio   args: ["mcp"]
     Use the absolute path of the binary if the client does not search PATH.

4. Verify. All four must succeed:
   - indexio --version                  prints a version
   - indexio stats                      lists the repositories and a file count above zero
   - indexio search "TODO" --limit 3    returns hits from my code
   - indexio usage                      prints a report (it is empty until I use the tools)

5. Tell me what was installed, where the data directory is, which repositories were
   indexed, and that I must restart my agent session so it picks up the new MCP server.
   Do not modify any file inside my repositories.

安装完成后,Agent 将获得以下工具:code_search、code_grep、find_symbol、who_calls、file_outline、read_span、impact_of_symbol、impact_of_diff 和 recall,并能根据索引在毫秒内作答。工具表介绍了每个工具。

安装

任选一种方式。每种方式都会提供 indexio 命令。

平台命令
Linux、macOScurl -fsSL https://raw.githubusercontent.com/IOServicesLabs/indexio/main/install.sh | sh
Windows(PowerShell)irm https://raw.githubusercontent.com/IOServicesLabs/indexio/main/install.ps1 | iex
npm(任何平台)npm install -g indexio
pip(任何平台)pip install indexio-cli
Dockerdocker pull ghcr.io/ioserviceslabs/indexio
Homebrew、winget暂不支持;请使用上面的脚本或软件包
从源代码安装(Rust 1.85+)cargo install --git https://github.com/IOServicesLabs/indexio indexio

脚本和 npm 软件包会为你的操作系统和 CPU 下载最新发行版的二进制文件,并校验其 SHA-256。pip wheel 会将二进制文件打包其中,每个平台各有一个 wheel。pip 软件包名为 indexio-cli,因为 PyPI 上的 indexio 属于无关项目;它安装的命令名为 indexio。容器映像无需安装即可运行:请参阅使用 Docker 部署。git 必须位于 PATH 中。无需服务、数据库或模型下载。

升级

再次运行相同的安装命令。~/.indexio 中的索引会保留,无需重新建立索引。

安装方式升级命令
脚本,Linux 或 macOScurl -fsSL https://raw.githubusercontent.com/IOServicesLabs/indexio/main/install.sh | sh
脚本,Windowsirm https://raw.githubusercontent.com/IOServicesLabs/indexio/main/install.ps1 | iex
npmnpm install -g indexio@latest
pippip install -U indexio-cli
Dockerdocker pull ghcr.io/ioserviceslabs/indexio:latest,然后重新创建容器
源码cargo install --git https://github.com/IOServicesLabs/indexio indexio --force

然后再完成以下两个步骤:

  1. 运行 indexio hook install。此命令会添加新版本带来的钩子并修复其他钩子,同时保留你自己的钩子。
  2. 重启 Agent 会话。运行中的会话会继续使用其 indexio 服务,直到会话重启。

运行中的会话不会妨碍升级:

  • 两种脚本会在会话使用二进制文件时替换它。在 Windows 上,install.ps1 会将正在运行的 indexio.exe 移至 indexio.old.exe,再复制新版本。
  • 在 Windows 上,npm 和 pip 会尝试就地覆盖二进制文件,但 Windows 不允许在文件运行时执行此操作。请先关闭 Agent 会话,或使用脚本。
  • 仍在运行旧版本的会话可以读取新版本写入的索引。
  • 首个新服务会在后台执行一次性工作:为尚无向量的文件生成嵌入,并写入会话卡片。调用不会被阻塞。

快速开始

三条命令。第三条仅适用于 Claude Code。

indexio add ~/code            # 1. index every git repository under a folder
indexio search parse_config   # 2. search it (add --mode hybrid for a question in words)
indexio setup claude          # 3. register the MCP server with Claude Code

然后重启 Claude Code。其会话现在可以使用 indexio 工具。再运行两条可选命令,可以进一步节省用量:

indexio hook install          # reads and greps of indexed files go through the index
indexio sync                  # run from cron or a timer to keep the index current

indexio stats 显示已建立索引的内容。indexio usage 显示索引提供的 Token 数量,并与内置工具原本会返回的数量进行对比。

所有数据都存储在一个目录中:--data-dir、$INDEXIO_DATA_DIR 或 ~/.indexio。

目录

  1. 让你的 Agent 安装
  2. indexio 的功能
  3. 添加来源
  4. 保持索引最新
  5. 嵌入
  6. 绝不会建立索引的内容
  7. 保护 HTTP API
  8. 团队索引:实时工作树覆盖层
  9. 企业设置
  10. 将 indexio 与 Claude Code 配合使用
  11. Token 节省情况:实测数据
  12. 影响分析
  13. 通过其他工具使用 indexio
  14. 命令参考
  15. 环境变量
  16. 使用 Docker 部署
  17. 架构
  18. 性能实测
  19. 限制
  20. 开发与发布

indexio 的功能

indexio 可以回答有关组织代码的三类问题。

类型示例方法
词法WalkState::new 定义在哪里?哪些 Rust 文件匹配 /fn parse_*/?三元组索引、正则表达式、精确标识符
语义如何缓冲打印机输出?在哪里刷新身份验证 Token?对语法感知的代码块进行向量搜索
混合任意问题词法、BM25 和语义搜索结果,通过倒数排名融合(RRF)进行融合

indexio 还了解代码中的定义和调用图。它可以告诉你某个符号定义在哪里、哪些位置调用了它,以及更改它会导致哪些部分出问题。

添加源

每次 add 都会立即为源建立索引并构建语义平面。

indexio add ~/code                             # every git repository under the folder, at any depth
indexio add /srv/checkouts/legacy-monolith     # a folder without git: indexed as-is
indexio add github:my-org                      # a GitHub organization or user
indexio add github:my-org/payments             # one GitHub repository
indexio add azdo:my-org/Platform               # an Azure DevOps project
indexio add https://gitlab.example.com/g/r.git # any git host

搜索:

indexio search parse_config
indexio search "how is backpressure applied" --mode hybrid

向 Agent 提供索引:

indexio mcp                           # MCP over stdio
indexio serve                         # HTTP API on http://127.0.0.1:7717

indexio 会在 <data-dir>/sources.json 中记住每个源。indexio sources 会列出各个源和已注册的仓库。indexio remove <source> 会移除一个源。

add 的实用标志:--limit 20(在大型组织中试运行)、--include-forks、 --include-archived、--full-clone、--dest DIR(远程克隆的存放位置)、--no-embed、 --exclude PATH、--pdf-text。

包含仓库和其他文件的文件夹。 一个文件夹通常包含 Git 仓库,以及不属于任何仓库的文件:笔记、规格说明、导出文件、PDF。indexio add 会为每个仓库分别建立索引,并将其他文件作为一个额外的仓库建立索引,该仓库使用文件夹的名称:

projectA/                     indexio add ~/projectA
├── backend/   (git)    →     repository "backend"
├── frontend/  (git)    →     repository "frontend"
├── docs/notes.md       →     repository "projectA": the contents
├── docs/spec.pdf       →     repository "projectA": the name only
└── old-copy/           →     left out with --exclude old-copy
  • 文本文件会按内容建立索引,大小不超过上限(文本为 256 KiB,代码为 4 MiB)。
  • 文档(PDF、Word、Excel、PowerPoint、OpenDocument、RTF、EPUB)和超出上限的文本文件只按名称建立索引。list_files 和搜索名称中的词语可以找到这些文件。对这类文件调用 read_span 时会提示其内容未建立索引;请直接读取文件。钩子允许对这类文件执行 Read 操作。
  • --pdf-text 会为这些 PDF 的文本层建立索引,而不是只索引文件名。从文档(Word、Google Docs、浏览器)导出的 PDF 有文本层,因此搜索可以找到其中的词语。扫描版 PDF 没有文本层,仍只会按名称建立索引:indexio 不执行 OCR。提取出的文本受文本大小上限限制。超过 64 MiB 或读取时间超过 20 秒的 PDF,仍只会按名称建立索引。系统会记住该选项并与源关联。
  • 图片、媒体文件、归档文件和其他二进制文件不会建立索引。
  • --exclude PATH(可重复指定,相对于文件夹的路径)会将文件夹排除在外:该文件夹不会作为仓库,其文件也不会建立索引。系统会记住排除项并与源关联;再次运行 add 时,排除项会累积。
  • 单独注册的文件夹不会被重复建立索引。

凭据。 indexio 只从环境中读取凭据:GITHUB_TOKEN(或 GH_TOKEN)和 AZDO_TOKEN(或 AZURE_DEVOPS_EXT_PAT、SYSTEM_ACCESSTOKEN)。它会将凭据发送到列表 API,并通过授权标头传给 git。凭据绝不会被写入克隆 URL、日志或 .git/config。公共仓库不需要令牌。

保持索引为最新状态

通过 cron 或计时器运行 indexio sync,例如每 15 分钟运行一次。每次运行都是 增量式的:

  • 克隆新仓库。
  • 按提交对有变更的 git 仓库进行增量索引。
  • 按内容哈希对有变更的普通文件夹进行增量索引。
  • 为新增和变更的分块生成嵌入。其他所有分块都会命中缓存。

包含 156 个仓库和 17,530 个文件的文件夹树,无需变更时只需 11 秒即可完成同步。

运行中的 indexio mcp 服务还会自动执行更多操作:

  • 它会监视会话所使用的仓库。文件发生变更时,会在每次调用前重新索引工作树。未提交和未跟踪的编辑会立即变为可搜索;其嵌入会在后台线程中于几百毫秒内生成。
  • 每 10 分钟,它会检查其他仓库的 HEAD 是否移动,并重新索引发生移动的仓库;还会按内容重新索引变更的普通文件夹(首次检查后,未变更的文件只需执行一次 stat)。
  • 如果会话启动时其文件夹尚未注册,会在 indexio add 后一分钟内接管该仓库。无需重启。
  • 如果会话的检出目录尚未注册,但位于三个或更多已注册仓库旁边,则会自动添加该目录(可以是 git 仓库,也可以是最多包含 20,000 个文件的普通项目文件夹,但绝不会是包含仓库的文件夹)。设置 INDEXIO_NO_AUTO_ADD,或创建 <data_dir>/auto-add-skip,即可关闭此功能。
  • 如果 read_span、code_grep、code_search 或 file_outline 的回答与本会话在过去 20 分钟内收到的某个回答完全相同,则会以一行提示的形式返回。再次发送相同调用即可返回完整文本;执行压缩或 /clear 会重置此状态。

嵌入

内置模型

默认嵌入器是随机索引(rindex)。这是一个语义模型,indexio 会在进程内基于你自己的代码构建它,使用 CPU 一次流式处理完成。它不需要训练基础设施、下载内容或 GPU。它能学习你所在组织的词汇:内部服务名称、代号和 API,这些都是预训练模型所不了解的。

每个新分块都是一次增量更新,因此 indexio sync 的开销很低。

在 20 个高难度概念查询(bench/)上的测量结果:混合搜索的 recall@5 为 60 %,MRR 为 0.397。 在相同查询上,仅使用词法搜索时,结果分别为 5 % 和 0.050。

可选的神经网络端点

要获得最高检索质量,可将 indexio 指向任意 OpenAI 兼容的嵌入端点, 例如运行在 vLLM 或 TEI 上的 Qwen3-Embedding。两个模型命名空间可以共存。

export INDEXIO_EMBED_BASE=http://embed.internal.example:8080
export INDEXIO_EMBED_MODEL=Qwen/Qwen3-Embedding-8B
export INDEXIO_EMBED_DIM=1024
export INDEXIO_EMBED_KEY=...                                    # only if the gateway needs it
indexio embed                                                   # builds vec/<model-id>.*.civec

嵌入按 (chunk hash, model id) 进行内容寻址。两个共享同一文件的仓库也会共享其嵌入。整个组织中的重复分块不会产生额外开销。

可选的重排序器

添加一个重排序器端点,例如运行在 TEI 或 vLLM 上的 Qwen3-Reranker。indexio 会在查询要求时使用它:indexio search --mode hybrid --rerank、通过 HTTP 使用 rerank=1,或通过 MCP 使用 rerank: true。如果没有端点,--rerank 不会产生任何效果。

export INDEXIO_RERANK_BASE=http://rerank.internal.example:8081
export INDEXIO_RERANK_MODEL=Qwen/Qwen3-Reranker-8B
export INDEXIO_RERANK_KEY=...

永不索引的内容

  • 没有已知代码、文档、配置、脚本或数据扩展名的文件,例如数据库、图像、归档文件和编译产物。
  • 二进制内容:如果文件前 8 KiB 中包含 NUL 字节,则跳过该文件。超过 4 MiB 的文件,以及超过 256 KiB 的文本文件。在普通文件夹中,这些文件和文档(PDF、Office)只按名称索引(参见添加来源)。
  • 构建产物、依赖项和缓存文件夹(node_modules、target、dist、vendor、任何包含 CACHEDIR.TAG 的文件夹)、被 git 忽略的文件、除配置文件夹(.github、.cargo、.vscode、.devcontainer 等)之外的隐藏文件夹、锁文件、压缩文件和源映射文件。
  • 凭据:.env 文件、私钥和证书(pem、key、p12、pfx、jks、gpg 等)、.netrc、.npmrc、Terraform 状态文件。先前版本索引的凭据文件会在下一次同步时移除。.env.example 和 *.pub 会保留。

recall 搜索的会话记录和运行日志会在存储前进行脱敏:看起来像密钥的设置值、URL 中的密码、API 密钥、承载令牌、JWT 和私钥块都会替换为 <redacted>。

保护 HTTP API

indexio serve 默认绑定到环回地址。

indexio serve --auth-token $(openssl rand -hex 32)               # one bearer token

如需按令牌控制仓库访问权限,请使用 ACL 文件:

cat > acl.json <<'EOF'
{"tokens":{"indexio-reader":{"allow":["team-*","core"]},"indexio-admin":{"allow":["*"]}}}
EOF
indexio serve --acl-file acl.json

使用 ACL 文件时:

  • 未知令牌会收到 HTTP 401。
  • /search、/symbol 和 /calls 中不符合允许模式的匹配结果会被移除。
  • 管理路由需要令牌具有 allow: ["*"]。其他令牌会收到 HTTP 403。
  • GET /health 始终开放。

注意。 如果绑定地址不是环回地址(--bind 0.0.0.0),则必须同时设置 --auth-token、$INDEXIO_AUTH_TOKEN 或 --acl-file,否则请求会被拒绝。通过 stdio 运行的 MCP 服务以本地用户身份运行,按设计不提供身份验证。请将面向网络的 MCP 服务置于你自己的网关之后。

团队索引:实时工作树覆盖层

indexio serve 可以将每位开发者未提交的更改叠加到共享索引上,因此,Agent 在回答你的问题时看到的是你的工作树——编辑内容、未跟踪文件、删除内容——而其他人仍然看到已推送的状态。开发者的机器上无需安装任何内容,只需在 Agent 执行框架中添加一个 shell 钩子:无需安装 indexio 客户端。

工作原理
  1. 服务端为已推送的仓库建立索引(常规的 add + sync)。
  2. Agent 执行框架中的 PreToolUse 钩子会在每次调用 indexio 工具前运行 integrations/claude-code/indexio-team-sync.sh。该脚本会将工作树(包括未跟踪文件)与远程默认分支的合并基准进行差异比较,并将补丁 POST 到 /team/worktree。
  3. 服务端会将补丁应用到一个按调用者用户名标识的小型覆盖层分片中,并叠加到基础分片之上:已更改的路径会覆盖基础索引,已删除的路径会被隐藏,其他所有内容都从共享索引读取。应用补丁时使用的是临时索引上的 git 底层命令,不会进行检出,也不会创建工作树。
  4. 最后一次已接受补丁的哈希会保存在磁盘上,因此,如果内容没有更改,重复调用不会产生任何开销。钩子不输出任何内容,且始终以 0 退出:它绝不会阻止工具调用,也不会污染 Agent 的上下文。

覆盖层在 INDEXIO_TEAM_TTL_DAYS 指定的时间(默认 7 天)内未更新后过期。

服务端设置

使用 ACL 文件启动服务,并为令牌设置 "user"——该名称用于标识覆盖层:

cat > acl.json <<'EOF'
{"tokens":{
  "tok-alice":{"allow":["*"],"user":"alice"},
  "tok-bob":  {"allow":["*"],"user":"bob"},
  "tok-ci":   {"allow":["*"]}
}}
EOF
indexio serve --acl-file acl.json --bind 0.0.0.0

没有 "user" 的令牌可用于读取,但不会使用覆盖层(见上文 CI)。如果使用单个 --auth-token 而非 ACL 文件,客户端可通过 X-Indexio-User 请求头指定用户。

开发者设置
indexio setup claude --team-sync              # writes the hook script, prints the entry
export INDEXIO_URL=https://indexio.example.com
export INDEXIO_TOKEN=tok-alice

setup claude --team-sync 会将钩子脚本写入 Claude 配置目录,并输出 settings.json 中的 PreToolUse 条目——匹配器为 mcp__indexio__.*,因此同步会在每次调用 indexio 工具前立即运行。若使用一个共享令牌而非每位用户各自的令牌,还要将 INDEXIO_USER 设置为要使用的用户名。

服务端强制执行的规则
  • 凭据路径(Lang::is_secret_path)绝不会被接受到覆盖层中,超过 32 MiB 的补丁也不例外。
  • ACL allow 模式会过滤每位用户可读取的内容:对 MCP,会直接过滤文档本身(渲染出的文本无法撤回);对 REST API,则通过过滤命中结果来实现。
  • 覆盖层用户名限制为 [A-Za-z0-9._@-],且不能以点号开头,因此覆盖层状态始终适合用作路径(SSO 身份通常是电子邮件地址)。
同时为多位开发者提供服务
  • 当 sync、reindex 或压缩操作写入新的分片时,服务会自行重新打开索引:在请求处理流程之外打开并预热新引擎,然后将其切换为当前引擎,因此无需调用 /admin/reload,也不会有请求等待。语义平面会在启动时及每次嵌入后解析。
  • 每个请求都在工作线程池上运行,小型查找不会占用共享并行池,因此即使其他用户正在执行大范围 grep 和混合搜索,find_symbol 或 read_span 也能在约 1 毫秒内返回结果。
  • 开发者的视图是在同一个共享索引上叠加其覆盖层:每位开发者约占 1 MB,几毫秒即可打开,并且每次上传后都会立即在后台打开。
  • 所有用户的搜索共用一个内容缓存:INDEXIO_CONTENT_CACHE_MB(默认值为 1024)会为每位用户的搜索缓存解压缩后的文件。
  • 对于接受 gzip 的客户端,响应会进行 gzip 压缩;钩子会以 gzip 格式上传大型补丁。
  • 搜索结果会优先显示会话自己的仓库,并且该仓库是 read_span 和 file_outline 的默认仓库,与本地会话一致:客户端在 X-Indexio-Repo 标头中指定的仓库(名称或 origin URL);若未指定,则使用开发者上次同步编辑所针对的仓库。
  • 如果未提供 diff,impact_of_diff 会将开发者已同步的工作树与其合并基点进行比较。
限制
  • 覆盖层是词法平面。向量搜索会对基础内容排序;对已跟踪文件的编辑会通过所有工具体现,而新建的未跟踪文件和删除操作则会显示在词法、符号和大纲工具中。
  • 基础版本必须是服务端已有的提交(与 origin/HEAD 的合并基点;否则依次使用 origin/main、origin/master 或上游分支),否则上传会被拒绝并返回 HTTP 409,并在下次调用时重试。服务端会自行获取 origin,且限制为每分钟最多一次。
  • 该钩子针对 Claude Code 进行了调优;其他开发框架也在逐步采用 PreToolUse 契约,但目前仅在 Claude Code 上经过测试。

企业级设置

以上所有操作都在一台机器上运行。同一个二进制文件可扩展至团队服务端或组织范围的部署;需要调整的部分是网络边缘和身份验证。

内网环境(LAN / VPN)

最简单的拓扑:在内部主机上运行一个 indexio 服务,并绑定到私有网络接口;在受信任的网络中,TLS 可选。

indexio serve --bind 10.0.0.5 --acl-file acl.json

开发者通过 INDEXIO_URL + INDEXIO_TOKEN 将团队同步钩子指向该服务。先从 ACL 文件开始;等分发令牌变成日常负担时,再切换到 SSO(见下文)。

面向互联网

服务使用纯 HTTP,并且在没有凭据时拒绝绑定到广域网络,因此应将其置于反向代理之后,由反向代理终止 TLS 并转发到环回端口。最简 Caddy 站点配置:

indexio.example.com {
	reverse_proxy 127.0.0.1:7717
}

然后使用 SSO 而非静态令牌提供服务:短期有效的身份令牌胜过笔记本电脑上的长期有效持有者令牌。

使用 OIDC 提供商进行 SSO(Entra ID、Google、Keycloak)

--oidc-issuer + --oidc-audience 可替代静态令牌模式。服务会从 <issuer>/.well-known/openid-configuration 发现提供商,缓存其 JWKS,并在 查询身份之前验证每个 bearer 令牌(RS256、签发者、受众、过期时间)——验证失败时拒绝访问:

cat > users.json <<'EOF'
{"users":{
  "[email protected]":{"allow":["*"],"user":"alice"},
  "[email protected]":{"allow":["payments-*"]}
}}
EOF
indexio serve \
  --oidc-issuer https://login.microsoftonline.com/<tenant-id>/v2.0 \
  --oidc-audience <application-id-uri> \
  --acl-file users.json --bind 0.0.0.0
  • 身份取自令牌的 email 声明(否则使用 sub)。users.json 中不存在的身份会收到 HTTP 401:用户配置仍由你现有的目录管理,并由你手动或通过同步任务镜像到此文件。
  • allow 的工作方式与令牌 ACL 完全相同;/admin 仍需要 *。 "user" 用于设置团队覆盖名称——未设置时使用电子邮件地址。
  • IdP 特定说明。Entra ID:创建一个公开了 API 的应用注册;受众为 Application ID URI。Google Workspace:使用 Web 应用 OAuth 客户端;受众为客户端 ID。Keycloak:在你的领域中创建一个机密客户端;受众为客户端 ID,签发者为 https://<host>/realms/<realm>。
  • 签名密钥轮换无需重启:遇到未知的 kid 时会按需重新获取 JWKS。
  • OIDC 模式会取代 --auth-token/INDEXIO_AUTH_TOKEN。请将它们留空。--oidc-audience 需要与 --oidc-issuer 一起使用。
试用:十分钟配置 Google Workspace

以下流程已于 2026-09-27 在运行中的服务上端到端验证。

  1. Google Cloud 控制台:创建一个项目(例如 indexio-pilot)。

  2. APIs & Services → OAuth consent screen:选择 External;应用名称设为 indexio pilot;添加 openid 和 email 范围;在 Test users 下添加你自己的 Google 账号——应用处于“Testing”状态时,只有测试用户才能授权。这是大家最容易忘记的一步。

  3. Credentials → Create OAuth client ID → Web application:添加 授权重定向 URI https://developers.google.com/oauthplayground。

  4. 在 https://developers.google.com/oauthplayground 获取真实的 ID 令牌:点击齿轮图标 → 选择“Use your own OAuth credentials” → 粘贴客户端 ID 和密钥 → 设置范围 openid email → Authorize APIs → 授权 → Exchange authorization code for tokens → 复制 id_token。

  5. 使用客户端 ID 作为受众,并在用户文件中填入你的电子邮件地址来启动服务:

    cat > users.json <<'EOF'
    {"users":{"[email protected]":{"allow":["*"],"user":"pilot-you"}}}
    EOF
    indexio serve --bind 0.0.0.0 \
      --oidc-issuer https://accounts.google.com \
      --oidc-audience <client-id>.apps.googleusercontent.com \
      --acl-file users.json
    
  6. 在任意位置进行快速测试:

    curl -H "Authorization: Bearer $ID_TOKEN" http://indexio.example.com:7717/search?q=parse
    # 200 with the token; 401 without. The token lives one hour — re-run
    # the playground exchange for a fresh one.
    

除了 Playground,任何能够运行 OAuth 授权流程的工具都可以生成服务接受的 bearer 令牌:IDE 插件、服务前端的简易登录页面,或 CI 任务。

GitHub

GitHub 并非适用于你自有 API 的通用 OIDC 提供商,但以下两种模式可以满足需求:

  • GitHub Actions → indexio 已支持 OIDC。 设置了 permissions: id-token: write 的任务可以请求 JWT(签发者为 https://token.actions.githubusercontent.com,受众由你指定),并将其作为 bearer 令牌传给 indexio 客户端。服务会像验证其他 OIDC 令牌一样验证它:

    indexio serve --oidc-issuer https://token.actions.githubusercontent.com \
      --oidc-audience indexio --acl-file users.json
    

    CI 机器无需保存静态令牌,而 users.json 中的允许列表会限定每个工作流可读取的内容。

  • 使用 GitHub 账号的人类用户。 在前面部署 OIDC 网关(将 oauth2-proxy 配置为使用 GitHub),并在网关与 indexio 之间保留 ACL 文件令牌;或者将人类用户的访问转移到 Entra ID / Google,由这些服务负责基于目录的用户配置。

人类用户与 Agent

为这两类用户使用不同的凭据:人类用户使用有效期较短的 SSO 令牌,Agent 和 CI 使用限定到其访问仓库的 ACL 文件令牌(allow: ["payments-*"])。撤销人类用户的访问权限,只需停用其目录账号;撤销 Agent 的访问权限,只需删除 ACL 文件中的一行。

在 Claude Code 中使用 indexio

indexio 通过 stdio 使用 MCP 通信。这是 Claude Code、Claude Agent SDK 和大多数编码 Agent 的原生工具接口。

注册服务

任选以下一种方式:

  • 运行 indexio setup claude。它会为此二进制文件和数据目录运行 claude mcp add,并打印供你添加到 CLAUDE.md 的指导内容。添加 --claude-md ~/.claude/CLAUDE.md 可将内容追加到文件中。

  • 运行 claude mcp add indexio -- /usr/local/bin/indexio mcp --data-dir /var/lib/ciindex。

  • 将 .mcp.json 提交到项目中:

    {
      "mcpServers": {
        "indexio": {
          "command": "/usr/local/bin/indexio",
          "args": ["mcp", "--data-dir", "/var/lib/ciindex"]
        }
      }
    }
    
安装钩子
indexio hook install

这会将五个 Claude Code 钩子写入 ~/.claude/settings.json:

钩子效果
PreToolUse,Bash如果通过 shell 读取或搜索已建立索引的文件(cat、sed -n、head、grep、rg、find,或只打开该文件的 python -c 或 node -e 单行命令),操作会被拒绝。拒绝提示会指出能返回相同结果的 indexio 调用。如果命令还会执行其他操作,例如运行脚本或构建,则会按输入内容执行。
PreToolUse,Read如果对超过 60 行的已建立索引文件执行整文件 Read,操作会被拒绝。拒绝提示会指出 file_outline 和 read_span。如果 Agent 工作树中的文件副本与已建立索引的文件完全相同,对副本执行 Read 也视为读取该文件。
PreToolUse,PowerShell对 Get-Content、Select-String 和 Get-ChildItem | Select-String 的读取和搜索应用相同规则,包括先执行 $c = Get-Content f,再使用行范围或循环的情况。
PreCompact会导入会话转录和 Claude Code 记忆笔记(~/.claude/projects/*/memory/),供 recall 工具使用。
SessionStart新会话会收到同一项目上一个会话的一行摘要,其中包含其标题、日期、变更文件、提交记录以及该会话卡片对应的 recall 调用(70–115 个 token)。压缩后,会话会收到自身的变更文件和提交记录(75–300 个 token)。恢复的会话不会收到内容。

Bash 钩子还会通过 indexio run 路由脚本和构建命令(python、node、npm、pytest、go、make …),避免长输出进入上下文:返回输出的首行、错误行和末行,并附上 runs:<repo>/<log> 指针,完整输出则可搜索 30 天。

如要按输入内容运行被拒绝的命令,请在命令后添加 # raw 并重新发送,或设置 INDEXIO_RAW=1。

工具
工具用途
code_search字面或正则搜索(mode: "lexical")、概念搜索("semantic"),或两者兼用("hybrid")。如果看起来像 grep 模式的字面查询没有匹配项,则会作为正则执行。包含多个词的字面查询如果没有匹配项,则会作为混合搜索执行。
code_grep在所有代码库中运行 grep -n:列出每个文件中的所有匹配行,并按相关性排序。可使用 repo 和 path 参数,或在末尾添加 repo:、path: 和 lang: 筛选条件。未配对的括号会按字面字符处理。
find_symbol查找函数、结构体、类、trait 或枚举的定义位置。
who_calls查找某个符号所有有记录的调用者。
semantic_search仅进行向量搜索。
list_files在索引路径中按目录折叠显示 glob 或子字符串匹配结果。不提供模式时会列出所有文件。
file_outline列出文件的所有定义及其起止行,或 Markdown 文件的标题。只需几百个 token,而不是整个文件。mod tests 的测试或 Test… 类中的测试会显示在一行:名称和起始行。
read_span读取索引文件的精确行范围。不提供结束行时,会读取起始行所在的整个定义。
impact_of_symbol查找所有代码库中某个符号的传递调用者。
impact_of_diff查找补丁或未提交工作树中涉及的调用者和导入者。
refresh_index在会话中增量重新索引、生成嵌入并重新加载。
recall搜索此前的会话、Claude Code 记忆笔记和已存储的命令输出:请求、回答、工具调用、结果、构建和测试日志。单个命中会附带对应的交互内容。设置 brief: true 后,会改为返回会话卡片:每个会话约 1,000 个 token,包含请求、修改的文件、提交和结果。query: "" 会返回此项目的最新会话。
index_stats查看已索引内容、索引时效,以及内置工具过去一周的使用情况。

服务会告知 Claude 每个 indexio 工具替代了哪个内置工具,以及哪些代码库已建立索引。随后,Claude 会自行将标识符路由到字面搜索,将问题路由到混合搜索。它通过大纲和行范围读取文件,而不是读取整个文件。

结果是紧凑的纯文本:按 repo:path 和 line: snippet 行分组、按目录折叠的文件列表,以及编号的行范围。不包含分数、排名或 JSON 标点。与相同命中结果的紧凑 JSON 相比,这种格式可减少 54 % 的 token。设置 INDEXIO_MCP_FORMAT=json 可使用 JSON 格式。

经测量的 token 节省

与内置工具在相同任务上的对比

bench/ab_readme.py 在相同代码库上,分别通过 Claude Code 的内置工具和 indexio 执行编码 Agent 会进行的任务。Grep 会以 path:line:text 的格式打印每个匹配项。Read 会打印整个文件,最多 2,000 行。Glob 会每行打印一个路径。token 数量使用 tiktoken 统计。测试涉及 3 个实际代码库(索引中共有 44 个代码库),每个代码库包含 10 个符号和 3 种文件类型,问题由模型编写。

任务内置工具indexio变化
查找符号定义位置(30)8141,421更差。find_symbol 会列出每个代码库中的结果。使用指定范围的 grep 只会找到一个。
列出符号的调用位置(30)2,0181,409−30 %
读取一个函数,已知文件但不知道行号(30)419,38041,612−90 %
列出一种类型的文件(9)12,2163,887−68 %
回答有关代码的问题(11)1,144,8421,883−99.8 %
所有任务1,579,27050,212−97 %

内置工具更省 token 的地方,差距只有几百个 token。耗费较多的地方,差距则高出几个数量级,因为 Read 会返回文件内容,而 Grep 会返回每个词的所有匹配项。

实际流量表现

服务会记录每次调用及其返回的字节数。对于内置工具存在机械对应项的调用,服务还会记录该工具本会返回的内容:

调用内置工具对应项
read_span对整个文件执行一次 Read,按文件版本计费:文件变更后再次计费
find_symbol、who_calls、code_grep、词法 code_search在会话的代码库中执行 Grep,取前 250 行
list_files将 Glob 结果作为绝对路径返回
file_outline无。计入成本。
混合搜索、recall、影响分析工具无。不作比较。

index_stats 以所有会话最近 7 天的数据结尾。indexio usage 会按工具和代码库打印相同的数据。作者的机器上,一天的实际编码会话中有 423 次可比较调用;实际提供了 22 万个 token,而内置工具本会返回 59.6 万个,减少了 63 %。仅 read_span 就减少了 71 %。

影响分析

indexio impact --symbol RateLimiter::acquire --depth 2     # transitive callers, all repositories
indexio impact --diff my-service                           # uncommitted working-tree changes
indexio impact --diff-file pr.patch --repo my-service      # a patch
indexio impact --file my-service:src/auth/token.rs         # every definition in a file, plus importers
indexio outline my-service:src/auth/token.rs               # definitions with line ranges
indexio span my-service:src/auth/token.rs 120:160          # exact lines

遍历会对索引持有的反向调用图执行广度优先搜索。索引时无需付出任何成本,查询可在毫秒内完成。补丁会通过 tree-sitter 扫描,仅对已更改文件中的定义进行映射。结果会排除这些已更改文件。系统会通过语言感知的导入查询找出导入它们的文件。

边基于名称建立。Foo::new 和 Bar::new 会共享节点 new。每个位置都包含其 caller、path 和 symbol,因此 Agent 可以进行筛选。扇出上限可限制枢纽名称的数量;当触发上限时,truncated: true 会告知你。

HTTP API 提供相同的接口:GET /impact/symbol、POST /impact/diff、GET /outline、GET /span。这些接口都会应用 ACL。

在其他工具中使用 indexio

任何 Agent 框架都可以使用 HTTP API。

curl 'http://127.0.0.1:7717/search?q=auth%20token%20refresh&mode=hybrid&limit=10'
curl 'http://127.0.0.1:7717/symbol/RateLimiter'
curl 'http://127.0.0.1:7717/calls/RateLimiter::acquire'

响应是命中项的 JSON 列表,包含代码库、路径、行、列、代码片段和得分。

命令参考

每条命令都接受 --data-dir DIR。默认值为 $INDEXIO_DATA_DIR,其次为 ~/.indexio。

索引代码
indexio add ~/code                          # every git repository under a folder
indexio add ~/notes                         # a folder without git: indexed as-is
indexio add github:my-org                   # a GitHub organization
indexio add github:my-org/payments          # one repository
indexio add azdo:my-org/Platform            # an Azure DevOps project
indexio add https://gitlab.example.com/g/r.git
indexio add github:my-org --limit 20 --no-embed     # trial run: 20 repositories, lexical only
indexio add github:my-org --dest /srv/clones        # where remote clones go
indexio index ~/code/one-repo --name one            # one repository, no source bookkeeping
indexio sources                             # sources and registered repositories
indexio remove github:my-org                # forget a source
保持最新
indexio sync                                # pull, discover, delta re-index, embed. Run from cron.
indexio sync --no-embed                     # lexical plane only
indexio reindex --repo payments             # one repository, from HEAD
indexio reindex --repo payments --worktree  # from the working tree, uncommitted edits included
indexio reindex --all
搜索
indexio search parse_config                                  # exact identifier
indexio search 'parse_config repo:payments lang:rust'        # filters: repo: lang: path: case:
indexio search '"token refresh"'                             # phrase
indexio search '/fn (parse|load)_config\(/'                  # regex
indexio search 'how is backpressure applied' --mode hybrid   # question
indexio search 'retry with exponential backoff' --mode semantic --limit 10
indexio search 'rate limiter' --mode hybrid --fusion combmnz # alternative fusion
indexio search 'rate limiter' --mode hybrid --rerank         # needs INDEXIO_RERANK_BASE
indexio search parse_config --json
符号和影响范围
indexio symbol parse_config                 # definitions
indexio calls parse_config                  # call sites
indexio impact --symbol parse_config        # transitive callers and importers, 2 hops
indexio impact --symbol parse_config --depth 3 --max-sites 1000
indexio impact --diff payments              # the uncommitted working tree
indexio impact --diff payments --base origin/main
git diff main | indexio impact --diff-file - --repo payments
indexio impact --file payments:src/config.rs
indexio impact --symbol parse_config --json
从索引读取
indexio outline payments:src/config.rs      # definitions with start and end lines
indexio span payments:src/config.rs 120     # the definition at line 120
indexio span payments:src/config.rs 120:160 # a range, 400 lines at most
嵌入
indexio embed                               # embed every repository, incrementally
indexio embed --repo payments
indexio embed --all --rebuild-model         # retrain the built-in model from scratch
indexio embed --max-chars 800               # smaller chunks
indexio embcas-stats                        # embedding cache statistics
indexio embcas-stats --coverage             # per repository: indexed files without vectors
启动服务
indexio mcp                                 # MCP over stdio
indexio mcp --repo payments                 # pin the session's repository
indexio serve                               # HTTP on 127.0.0.1:7717
indexio serve --port 8080 --auth-token $(openssl rand -hex 32)
indexio serve --bind 0.0.0.0 --auth-token "$TOKEN"          # beyond localhost: a token is mandatory
indexio serve --acl-file acl.json
indexio serve --oidc-issuer https://idp.example.com --oidc-audience indexio-api --acl-file users.json
# team worktree overlays: POST /team/worktree (see "Team indexing" above)
Agent 设置
indexio setup claude
indexio setup claude --claude-md ~/.claude/CLAUDE.md
indexio setup claude --team-sync            # also install the team worktree-sync hook
indexio hook install
indexio hook install --print-only
indexio sessions                            # import Claude Code transcripts for recall
indexio sessions --project C--Users-me-code-payments
运行命令而不填满上下文
indexio run -- python scripts/probe.py --fast    # short output printed as is; long output stored + digested
indexio run -- npm test                          # the Bash hook does this rewrite for you
indexio run --cwd ~/code/payments -- pytest -q

已存储的日志路径格式为 runs:<repo>/<stamp>-<command>.log:对其使用 read_span,即可读取摘要省略的行;recall 可在之后找到它。日志会在 INDEXIO_RETAIN_DAYS 指定的天数后滚动清除。

测量与维护
indexio stats                               # repositories, files, shards, index size
indexio usage                               # last 7 days of MCP calls, against the built-in tools
indexio usage --days 1 --json
indexio compact                             # merge shards
indexio compact --vectors                   # also merge vector segments
indexio cas-stats

环境变量

变量作用
INDEXIO_DATA_DIR数据目录。默认值为 ~/.indexio。
INDEXIO_REPOMCP 会话所操作的仓库。默认值为包含当前工作目录的仓库。
INDEXIO_BINDserve 的绑定地址。若不是回环地址,则需要令牌或 ACL。
INDEXIO_AUTH_TOKENserve 的 Bearer 令牌。
INDEXIO_TEAM_TTL_DAYS团队工作树覆盖在未更新的情况下保留的天数。默认值为 7。
INDEXIO_CONTENT_CACHE_MBserve 的解压内容缓存,由所有用户共享。默认值为 1024。
INDEXIO_URL、INDEXIO_TOKEN、INDEXIO_USER团队同步钩子的服务器、Bearer 令牌和操作用户(仅使用共享令牌时)。
INDEXIO_EMBED_BASE、INDEXIO_EMBED_MODEL、INDEXIO_EMBED_DIM、INDEXIO_EMBED_KEYOpenAI 兼容的嵌入端点。
INDEXIO_RERANK_BASE、INDEXIO_RERANK_MODEL、INDEXIO_RERANK_KEY重排端点。
GITHUB_TOKEN 或 GH_TOKEN、AZDO_TOKEN远程来源的凭据。绝不会写入磁盘。
INDEXIO_MCP_FORMAT工具结果格式:text(默认值)或 json。
INDEXIO_FUSE_WEIGHTS混合检索各路权重 lex,bm25,sem。默认值为 0.5,1.0,0.3。
INDEXIO_EXPAND启用同义词查询扩展。默认关闭;测得效果更差。
INDEXIO_RETAIN_DAYS运行日志和渲染后的会话转录保留的天数。默认值为 30,设为 0 则永久保留。
INDEXIO_NO_USAGE不写入使用情况日志。用于基准测试。
INDEXIO_NO_AUTO_ADD会话不会将未注册的工作副本添加到索引中。
INDEXIO_RAW按原样运行已挂钩的 Bash 命令。
INDEXIO_HOOK_DEBUGBash 钩子会在 stderr 中说明其决策。

使用 Docker 部署

发布工作流会为 linux/amd64 和 linux/arm64 发布 ghcr.io/ioserviceslabs/indexio。镜像以非特权用户身份运行。数据目录为 /data。镜像中包含 git 和 CA 证书。

如果你要为自己的 Agent 索引自己的机器,请改为安装二进制文件。 该容器适用于由多人或多台机器通过 HTTP 查询的共享索引。在你自己的笔记本电脑上使用它,会让你失去 Bash 钩子、工作树即时刷新功能,还要多一层路径转换,却没有任何收益。请使用上文的安装。本节其余内容适用于共享场景,以及无需安装即可试用 indexio 的情况。

只使用 Docker,即可为本机上的文件夹建立索引并搜索:

docker run --rm -v indexio-data:/data -v ~/code:/repos:ro ghcr.io/ioserviceslabs/indexio add /repos
docker run --rm -v indexio-data:/data ghcr.io/ioserviceslabs/indexio search parse_config
docker run --rm -v indexio-data:/data ghcr.io/ioserviceslabs/indexio stats

提供 HTTP API:

docker run -d --name indexio -p 127.0.0.1:7717:7717 -v indexio-data:/data   -e INDEXIO_AUTH_TOKEN="$(openssl rand -hex 32)" ghcr.io/ioserviceslabs/indexio
curl -H "Authorization: Bearer $TOKEN" 'http://127.0.0.1:7717/search?q=parse_config'

将容器作为 MCP 服务提供给 Agent(通过 docker run -i 使用 stdio):

claude mcp add indexio -- docker run -i --rm -v indexio-data:/data -v ~/code:/repos:ro ghcr.io/ioserviceslabs/indexio mcp
容器如何为你机器上的代码建立索引

除了你绑定挂载到容器中的内容外,容器无法访问你的磁盘。两个挂载点负责这项工作,而且它们并不对称:

挂载点用途所需访问权限
-v ~/code:/repos:ro要建立索引的代码。可以挂载任意内容:单个仓库、检出根目录,或使用多个 -v 标志。只读即可。建立索引绝不会写入源代码。
-v indexio-data:/data索引本身。需要可写权限。这是 Docker 卷,不是主机上的文件夹。

indexio add /repos 会遍历挂载点,在任意深度注册其下的每个 git 仓库,并为每个仓库建立索引。没有 git 仓库的文件夹会作为普通目录树建立索引。每个仓库都会记录在 /data/repos/<name>.json 中,路径采用容器看到的路径——/repos/my-service,而不是 C:\Users\you\code\my-service。搜索结果采用相对于仓库的路径(my-service:src/main.rs),因此不影响搜索,但索引中不会包含主机路径。

建立大型索引前,有几点值得了解:

  • 容器索引和主机索引不能互换。 如果主机二进制指向在容器中构建的数据目录,记录的 /repos/... 路径在主机上并不存在,因此重新建立索引以及对未建立索引的内容调用 read_span 都会失败。请选择一种方式并始终使用,或保留两个数据目录。
  • 索引的新鲜度来自同步循环,而不是文件监视器。 对本地文件夹运行 indexio sync 会重新发现根目录下的仓库,并根据磁盘上的当前内容增量更新索引。它不会克隆、拉取或写入你的源代码——只有远程源类型(github:、azdo:、git URL)会获取内容。docker-compose.yml 中的 sync 服务每 15 分钟运行一次。
  • 自动刷新无法穿透挂载。 indexio mcp 通常会监视当前仓库,并在编辑后约 25 ms 内重新为工作树建立索引。文件系统事件无法可靠地穿过 Windows 或 macOS 主机上的 Docker Desktop 绑定挂载,因此在容器中,应将索引视为截至最近一次 sync 或 refresh_index 调用时的最新状态,而不是截至最近一次按键时的状态。这也是让主机二进制与正在编辑代码的 Agent 一起运行的主要原因。

Bash 钩子(indexio hook install)也需要主机二进制:它们会在你的 shell 中改写命令,而容器无法看到 shell。

在 Windows 上,请使用 PowerShell 而不是上面的 bash 命令来生成服务令牌:

$bytes = New-Object byte[] 32
[System.Security.Cryptography.RandomNumberGenerator]::Fill($bytes)
$env:INDEXIO_AUTH_TOKEN = [System.BitConverter]::ToString($bytes).Replace('-','').ToLower()

该镜像将 INDEXIO_BIND=0.0.0.0,因此如果没有 INDEXIO_AUTH_TOKEN 或 --acl-file,serve 会拒绝启动:包含你所有源代码的索引不能在未进行身份验证的情况下对外发布。不要通过在容器中设置 INDEXIO_BIND=127.0.0.1 来“修复”这一问题——此时服务会绑定到容器自身的回环地址,发布的端口将无法访问它。请将绑定地址保持为 0.0.0.0,设置令牌,并将端口发布到 127.0.0.1:7717:7717,这样只有你的机器可以连接。

持续索引一个文件夹

假设代码位于 C:\codebuilds,你希望按定时计划重新索引,并让 Agent 通过 MCP 查询。这里不需要身份验证令牌:该令牌用于保护 serve 和 HTTP API,而此设置不会运行它。

注册文件夹并构建一次索引。add 会将 /repos 记录为持久化源,之后的同步会重新读取该源:

docker run --rm -v indexio-data:/data -v C:/codebuilds:/repos:ro `
  ghcr.io/ioserviceslabs/indexio add /repos

然后让容器保持运行,并循环重新同步。镜像的入口点是二进制文件,因此需要覆盖它以进入 shell:

docker run -d --name indexio-sync --restart unless-stopped `
  -v indexio-data:/data -v C:/codebuilds:/repos:ro `
  --entrypoint /bin/sh ghcr.io/ioserviceslabs/indexio `
  -c "while true; do indexio sync || true; sleep 900; done"

让 Agent 指向相同的卷。MCP 服务使用标准输入/输出,因此不需要端口和令牌:

claude mcp add indexio -- docker run -i --rm -v indexio-data:/data -v C:/codebuilds:/repos:ro ghcr.io/ioserviceslabs/indexio mcp

使用 docker run --rm -v indexio-data:/data ghcr.io/ioserviceslabs/indexio stats 检查;它应列出在 C:\codebuilds 下找到的所有仓库。

挂载路径使用正斜杠(C:/codebuilds),并确保已在 Docker Desktop 的文件共享设置中共享该驱动器。如果 15 分钟的 sleep 间隔太长,可以调低;但只有在下一次同步后,编辑才会显示。因此,如果你希望索引在编辑时实时跟踪更改,请改为运行主机二进制文件。

要使用 docker-compose.yml 实现相同功能,请设置文件夹并仅启动同步服务:

$env:INDEXIO_REPOS = "C:/codebuilds"
docker compose up -d sync
docker compose exec sync indexio add /repos

Compose 会在选择服务之前插入整个文件,因此即使你不启动 indexio 服务,也必须设置 INDEXIO_AUTH_TOKEN,文件才能解析。按上述方式设置它,或使用普通的 docker run 形式。

docker-compose.yml 会在一个卷上运行两个服务:

  • indexio:HTTP API,仅在 localhost 上发布。
  • sync:每 15 分钟运行一次 indexio sync。GITHUB_TOKEN 和 AZDO_TOKEN 会传递给容器。
  1. 将要索引的文件夹放在 ./repos 下。
  2. 启动服务:INDEXIO_AUTH_TOKEN=$(openssl rand -hex 32) docker compose up -d
  3. 一次性索引这些文件夹:docker compose exec sync indexio add /repos

容器绑定到 0.0.0.0。如果没有 INDEXIO_AUTH_TOKEN 或 ACL 文件,二进制文件将拒绝启动。docker build -t indexio . 会从源代码构建相同的镜像。

架构

            ┌──────────────────────────── data dir ────────────────────────────┐
 git repos  │  shards/*.cidx   immutable mmap'd index shards (single file)      │
    │       │  cas/            global content-addressed store (blake3 keyed)    │
    ▼       │  embcas/         embedding CAS: (chunk-hash, model-id) → vector   │
 indexio-ingest  │  vec/*.civec     int8 vector segments + binary prescan            │
  git delta │  repos/*.json    per-repo indexing state (last commit)            │
    │       └──────────────────────────────────────────────────────────────────┘
    ▼
 Lexical plane                             Semantic plane
 ─────────────────────                     ──────────────────────────
 trigram inverted index                    tree-sitter chunking
 FST term dictionary, LEB128 postings      pluggable embedder (built-in model
 symbols + call edges (tree-sitter,          or any OpenAI-compatible endpoint)
 6 languages, AST discarded at once)       embedding CAS (org-wide dedup)
 query planner: required literals,         mmap'd binary prescan + int8 rescore
  galloping intersection, verify, BM25
            └─── indexio-query: RRF fusion → optional reranker ───┘
                                   │
                    CLI · HTTP (axum) · MCP (stdio JSON-RPC)

五项设计决策:

  1. 采用三元组索引,而非常驻 AST。 indexio 会解析 AST,从中提取符号、调用边和代码块边界,然后将其丢弃。常驻 AST 的空间开销是文本大小的 20 到 50 倍。三元组分片的开销是代码库大小的 1 到 3 倍,查询仅需几毫秒。
  2. 采用全局内容寻址存储。 提取的制品和嵌入向量在整个组织范围内按内容哈希寻址。分支、供应商副本或重复文件只会被索引和嵌入一次。
  3. 采用带墓碑标记的不可变 mmap 分片。 构建索引时会写入新的分片。删除操作会设置位图墓碑标记。indexio compact 会在后台合并分片。读取操作不会阻塞写入操作。
  4. 采用原生 Git 的增量索引。 重新索引时会遍历 HEAD 树的差异。未更改的 blob 不会再次读取、解析或嵌入。单文件提交的重新索引耗时不到一秒。
  5. 采用独立的语义层。 向量索引由存储和分片派生而来。你可以更改嵌入模型或重建该层,而无需改动词法索引。

数据布局:

<data-dir>/
  shards/*.cidx          immutable index shards (mmap'd)
  cas/v2/<hh>/<rest>     content-addressed extracted artifacts (zstd)
  embcas/<model>/        content-addressed embeddings, one pack per model
  vec/<model>.*.civec    int8 vector segments with a binary prescan
  bm25/*.cibm25          chunk-level BM25 sidecar (mmap'd)
  sem/*.rimodel          the built-in semantic model (mmap'd)
  repos/<name>.json      per-repository state
  sessions/              rendered session transcripts for recall (30-day window)
  runs/<repo>/*.log      command output kept by `indexio run` (30-day window)
  usage/<day>.jsonl      the MCP usage log

实测性能

在作者的机器上,对包含 44 个仓库、10,000 个文件的数据目录测得的存储和启动性能(docs/SPEC-P10.md):

指标之前之后
143 MB 源码的数据目录大小5.3 GB1.0 GB
服务启动:耗时、读取字节数、RSS188 ms、302 MB、417 MB10 ms、0 MB、74 MB
混合查询,进程已预热130 至 190 ms7 至 14 ms
正则表达式 grep,进程已预热424 ms28 ms
find_symbol、who_calls12 ms0.5 ms
编辑后工作树重新索引不可用约 25 ms

通过 shell 和索引执行相同的查找(bench/ab_speed.py;在本仓库中,一个包含 1,300 个文件的 TypeScript 应用得出了相同的结果)。墙上时钟时间,取 10 次测量的中位数;索引侧包含 MCP 传输耗时:

查找Shellindexio
标识符 grep(rg -n)32 ms0.6 ms
正则表达式 grep(rg -n)34 ms2.5 ms
对应用执行 grep -rn26 至 860 ms0.7 ms
读取 120 行(sed -n)23 ms0.3 ms
find -name25 至 410 ms0.9 ms
查找定义33 ms0.1 ms

每次 Bash 工具调用在命令运行前,还要支付约 15 ms 的 shell 开销,以及两个 PreToolUse hook 合计约 30 ms 的开销。MCP 调用无需支付这些开销。在实际流量下(7 天内 5,355 次调用),read_span 的中位数为 0 ms,code_search 为 12 ms,code_grep 为 29 ms;第 90 百分位数低于 130 ms。

在配备 2 个核心和 4 GB RAM 的沙盒中测得的去重效果:

场景结果
索引 serde,然后索引一个完全相同的 forkFork:缓存命中率 100 %,耗时 3.0 s,而非 10.0 s
索引 ripgrep,然后索引一个有 1 个文件变更的 forkFork:缓存命中率 100 %,耗时 2.5 s,而非 6.6 s
为 4 个仓库重新生成嵌入,共 19,977 个分块实例4,731 个唯一嵌入,去重率 76 %

同一沙盒中的词法延迟:罕见标识符 21 ms,常见词语 81 ms,正则表达式 254 ms。单文件提交的增量重新索引耗时 792 ms。

20 个概念查询的语义质量(bench/known_answers20.json、bench/eval_modes.py):

模式MRRrecall@5
词法0.0505 %
语义0.29340 %
混合0.39760 %

经测量后弃用:CombMNZ 融合的得分低于 RRF。离线重叠重排器的得分低于单独使用混合模式。同义词词典查询扩展的得分低于纯语义搜索。IVF 预扫描没有带来加速。默认情况下均不启用。

限制

  • 不支持基于类型解析的交叉引用。 调用边基于名称。new、get 和 run 等重载名称会产生噪声。请按 caller 和 path 筛选。
  • impact --diff 不包含未跟踪文件。 git diff 不会包含这些文件。请先暂存,或传入补丁。
  • 内置模型并非神经网络模型。 它实用且成本低。如需神经网络级质量,请设置 INDEXIO_EMBED_BASE。
  • MCP 不提供身份验证。 它以本地用户身份通过 stdio 运行。请勿在没有网关的情况下将其暴露到网络。
  • 没有 Web UI,也不支持分布式查询。 一个二进制文件,一个数据目录。

开发与发布

cargo test --workspace                  # every crate, 325 tests
cargo clippy --workspace --all-targets
tools/build-release.sh                  # release build; remaps local paths out of the binary

Crate:indexio-types(共享类型、posting 编解码器)、indexio-core(n-gram、验证、查询字面量)、indexio-index(分片、合并、墓碑)、indexio-symbols(适用于 6 种语言的 tree-sitter 符号、调用和代码块)、indexio-ingest(git 增量、内容存储、来源)、indexio-embed(嵌入器、嵌入存储、向量索引、流水线)、indexio-query(规划器、排序、融合、影响分析)、indexio(命令行、HTTP、MCP、钩子)。设计说明位于 docs/;docs/DEVELOPMENT.md 是运维手册。

CI(.github/workflows/ci.yml)会在每次推送和拉取请求时运行。它会检查清单能否解析、脚本能否编译,以及工作流是否有效。运行 90 秒后会被终止。构建和测试套件在本地运行;它们无法在该时限内完成。

发布(.github/workflows/release.yml)基于 main:

  1. 在 Cargo.toml([workspace.package])中设置 version。
  2. 合并到 main。不到 90 秒,工作流便会创建标签 v<version> 和一份带有自动生成说明的 GitHub release。如果合并时版本未变更,则不会执行任何操作。
  3. 随后,同一工作流会为 Linux(x86_64、aarch64)、macOS(Apple silicon、Intel)和 Windows(x86_64)构建二进制文件,将每个归档文件及其 .sha256 文件和一个 pip wheel 附加到 release,将容器镜像发布到 ghcr.io;如果存在 NPM_TOKEN 密钥,则发布 npm 包;如果存在 PYPI_API_TOKEN 密钥,则将 indexio-cli wheels 发布到 PyPI。这些任务会编译 Rust,并需要几分钟。

tools/release-assets.sh 可在维护者的机器上为工作流未覆盖的平台构建并附加一个资源。

许可证

Apache License 2.0。请参阅 LICENSE 和 NOTICE。

评论

支持 Markdown

第一次接触 MCP?

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

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