内容
# mcp-gtags-server
> **让你的 AI 代理不再使用 grep。给它一个索引。**
[](https://pypi.org/project/mcp-gtags-server/)
[](https://github.com/harshithsunku/mcp-gtags-server/actions/workflows/ci.yml)
[](https://github.com/harshithsunku/mcp-gtags-server/actions/workflows/eval.yml)
[](https://www.python.org/downloads/)
[](LICENSE)
[](https://modelcontextprotocol.io/)
[](https://www.gnu.org/software/global/)
[](https://cursor.com/en/install-mcp?name=gtags&config=eyJjb21tYW5kIjoidXZ4IiwiYXJncyI6WyJtY3AtZ3RhZ3Mtc2VydmVyIl19)
mcp-name: io.github.harshithsunku/mcp-gtags-server
```bash
# 如果你还没有安装 uv(一次性,无需 sudo):
curl -LsSf https://astral.sh/uv/install.sh | sh
```
```json
{ "mcpServers": { "gtags": { "command": "uvx", "args": ["mcp-gtags-server"] } } }
```
*一个用户级配置项,无需 sudo,无需预安装任何内容 — 整个工具链在首次使用时自动安装到用户空间,每个你打开的仓库都自动提供服务。*
每个 AI 编码代理(如 Claude Code、Cursor、Codex 等)都以相同的方式回答“这个函数在哪里定义?”:**grep 整个代码树**。在百万行 C/C++ 代码库中,这意味着每次查询都需要完整扫描,输出是一堆无关信息:注释、字符串字面量和不相关的匹配结果,直接倾倒到模型的上下文窗口中。
**mcp-gtags-server** 用索引查找取代了这些扫描,索引由 [GNU Global (gtags)](https://www.gnu.org/software/global/) 提供支持 — 与开发者信任的数十年来的相同标签引擎内核和系统 — 通过 [Model Context Protocol](https://modelcontextprotocol.io/) 向代理暴露。适用于 LSP 工具无法处理的代码库:内核级 C/C++、无法编译的树、无法 sudo 的机器。
- **每查询约快 100 倍** — 毫秒级而不是秒级,适用于任何代码库大小
- **噪音极少** — 定义,而不是 7873 行匹配结果
- **支持内核** — `#ifdef` 保护堆栈,带有 `.config` 过滤,宏生成的符号(`sys_read` → 其 `SYSCALL_DEFINE3` 站点),解析器错过的定义通过其 `EXPORT_SYMBOL` 站点恢复
- **零索引管理** — 第一次查询构建索引,每次查询自动刷新
- **CI 中验证正确性** — 65 个用例的黄金评估,涵盖所有 11 种工具,对比固定的内核:100% 召回率,100% 精确度@1 ([docs/capability.md](docs/capability.md))
- **适用于 MCP 的任何地方** — Claude Code、Claude Desktop、Cursor、任何 MCP 客户端
## 数字(真实的 Linux 内核,非玩具)
在完整的 Linux 内核checkout 上进行测量 — **65,163 个 C/C++ 文件,3710 万行** — 暖页缓存:
| 代理询问的问题 | `grep -rn` | gtags(此服务器) | 上下文消耗 |
|---|---|---|---|
| `tcp_v4_rcv` 在哪里定义? | 1.40 秒 | **0.01 秒** | 8 行 → **1 行** |
| `kmalloc` 在哪里定义? | 1.62 秒 | **0.01 秒** | 7873 行 → **5 行** |
| 哪些引用 `kmalloc`? | 1.62 秒 | **0.10 秒** | 7873 行噪音 → 2744 个真实站点(或**排名的文件摘要**) |
| 显示 `tcp_v4_rcv` 的实现 | *读取一个 3500 行文件* | **`get_symbol_body`** | **恰好是 271 行函数** |
| 哪些调用 `ext4_mark_inode_dirty`? | 245 行原始匹配行 | **`find_callers`** | **62 个去重调用函数,带计数** |
| `sys_read` 真正定义在哪里? | *无答案 — 名称是宏生成的* | **0.03 秒** | `fs/read_write.c SYSCALL_DEFINE3(read, ...)`,标记 `resolved_via` |
| `mutex_lock` 在哪里定义? | 24774 行噪音匹配行 | **0.2 秒** | `kernel/locking/mutex.c:314` — 通过 `EXPORT_SYMBOL` + ctags 恢复 |
| `ksys_read` 是否达到 `rw_verify_area`? | *多轮 grep + 读取* | **0.6 秒** | 最短调用链,带有每个调用站点的文件:行 |
| 我的未提交的 diff 影响什么? | *无法回答* | **0.1 秒** | `blast_radius`:更改的函数 + 调用者,按距离排名 |
一次性索引构建:**66 秒** 适用于整个内核。编辑后的增量刷新:远低于 1 秒。使用 [`scripts/benchmark.sh`](scripts/benchmark.sh) 自身重现:
```bash
./scripts/benchmark.sh /path/to/linux tcp_v4_rcv kmalloc ext4_readdir
```
速度很好。真正的胜利是**精度**:获取 5 个精确行而不是 7873 个噪音行,保留上下文窗口用于实际推理。
答案是*经过测量*的,而不是假设的:CI 在每次推送时都会运行 [65 个用例的黄金评估](evals/golden.jsonl),对比固定的内核 v6.16 — 目前**100% 召回率,100% 精确度@1**,涵盖定义、宏解析、导出恢复、引用、调用者、被调用者、定义体、`#ifdef` 保护和可达性,涵盖所有 11 种工具。完整的 方法论、数字和诚实的局限性位于 [docs/capability.md](docs/capability.md)。
## 快速开始(60 秒)
### 选项 A — 一个配置项(推荐)
添加服务器**一次,在用户级别**,然后完成 — 无需安装程序,无需预安装 gtags,无需每个仓库设置。唯一的先决条件是 [`uv`](https://docs.astral.sh/uv/)(或使用选项 B,它为您安装)。
```bash
# Claude Code(每个设备一次,所有仓库):
claude mcp add --scope user gtags -- uvx mcp-gtags-server
```
```json
// Cursor (~/.cursor/mcp.json),或任何 MCP 客户端的全局设置:
{
"mcpServers": {
"gtags": { "command": "uvx", "args": ["mcp-gtags-server"] }
}
}
```
或点击页面顶部的 **Cursor 一键安装** 徽章。
在第一次工具调用时,服务器会自行引导其他所有内容:GNU Global(预构建的用户空间二进制文件 — 无编译器,无 sudo),universal-ctags 和 Pygments,都安装到 `~/.gtags-mcp` 中。在此一次性安装运行期间(大多数平台上的几秒钟),工具调用会返回一个“工具链正在安装 — 稍后重试”的状态,而不是失败。你可以退出 `--no-auto-setup` 或 `GTAGS_MCP_AUTO_SETUP=0`。
预构建的 Linux 二进制文件在任何具有 glibc ≥ 2.28 的发行版上运行(RHEL/Rocky 8+,Ubuntu 18.10+,Debian 10+)。在较旧的主机上 — 或如果下载的二进制文件安装后执行检查失败 — 安装程序会自动从源代码编译 GNU Global,只需要 `make` 和 C 编译器。
### 选项 B — 一行安装程序(共享后台服务器)
**一条命令。无需 sudo。在任何地方都有效** — 受限的公司机器、容器、构建服务器:
```bash
curl -fsSL https://raw.githubusercontent.com/harshithsunku/mcp-gtags-server/main/scripts/install.sh | bash
```
所有内容都位于你的主目录中 — 服务器(通过 `uv`)、GNU Global、universal-ctags 和 Pygments(在 `~/.gtags-mcp` 中)。当它完成时,一个**共享后台 HTTP 服务器**正在运行,同一个机器上的每个客户端和 IDE 窗口都可以指向它(`http://127.0.0.1:8383/mcp`),并且确切的客户端配置会打印到你的控制台。
**重新运行相同的命令:**
- 已更新? → *"已经安装并更新 — 无需安装"*,并且再次打印配置。
- GitHub/PyPI 上有新版本? → 软件包更新,旧版 gtags 工具链被自动删除并重新安装,背景服务器在新版本上重启。
### 一个配置,多个仓库
无论你如何安装它,**一个用户级条目为你提供所有仓库的服务** — 20 个仓库需要零额外的安装和零额外的配置。每个工具调用都会将项目根目录解析到以下顺序:
1. 工具调用上的 `project_root` 参数(代理传递此参数以定位任何树)
2. `--root` 标志 / `GTAGS_MCP_ROOT` 环境变量
3. 配置文件中的 `root` → *(注意:固定根目录会破坏多仓库)*
4. **客户端的工作区根目录**(MCP 根目录协议)— IDE 广告其打开的文件夹会自动获得正确的仓库,甚至在共享 HTTP 服务器上;如果有多个文件夹打开,代理被要求传递 `project_root`
5. 从服务器的工作目录向上遍历到最近的 `.git`/`GTAGS` — 这就是为什么由 Claude Code/Cursor 在仓库中启动的 stdio 服务器直接工作
就是这样。无需索引步骤,无需配置。询问你的代理“哪些调用 `tcp_v4_rcv`?”— 任何仓库中的第一个查询都会自动构建该仓库的索引,随后的每个查询都在毫秒内回答。随时运行 `mcp-gtags-server doctor` 以查看服务器检测到的内容,或运行 `mcp-gtags-server config` 以重新打印客户端配置。
<details>
<summary><b>后台服务器详细信息</b>(网络访问、端口、生命周期)</summary>
安装程序运行 `mcp-gtags-server --transport http --host 127.0.0.1 --port 8383` 在后台(pid:`~/.gtags-mcp/server.pid`,日志:`~/.gtags-mcp/server.log`)。安装程序的环境变量:
| 变量 | 默认值 | 含义 |
|---|---|---|
| `GTAGS_MCP_PORT` | `8383` | HTTP 端口 |
| `GTAGS_MCP_HOST` | `127.0.0.1` | 绑定地址 — 设置 `0.0.0.0` 以从 `http://<machine-ip>:8383/mcp` 访问服务器 |
| `GTAGS_MCP_NO_SERVER` | 未设置 | `1` = 不要启动后台服务器 |
**安全注意:** HTTP 端点是无身份验证的。它默认绑定 localhost;仅在你信任的网络上绑定 `0.0.0.0`。
</details>
<details>
<summary><b>手动安装</b>(首选系统软件包,或已有 Global)</summary>
```bash
# 1. GNU Global — EITHER 用户空间(无需 sudo):
mcp-gtags-server setup
# OR 系统软件包:
sudo apt install global # Debian/Ubuntu
sudo dnf install global # Fedora
brew install global # macOS
# 2. 服务器:
uv tool install mcp-gtags-server # 或:pip install mcp-gtags-server
```
服务器按此顺序查找二进制文件:`--bin-dir`/`GTAGS_MCP_BIN_DIR`/config `bin_dir` → `~/.gtags-mcp/bin` → `PATH` → `~/.local/bin`.
</details>
<details>
<summary><b>Claude Desktop</b> — 扩展包或手动配置</summary>
最简单:从 [最新版本](https://github.com/harshithsunku/mcp-gtags-server/releases/latest) 下载 `mcp-gtags-server.mcpb`,将其拖入 Claude Desktop 的设置 → 扩展,然后在设置屏幕中选择你的项目文件夹。gtags 工具链在首次使用时自行安装。
或手动添加到 `claude_desktop_config.json`(固定项目,因为 Desktop 不会在你的仓库中启动):
```json
{
"mcpServers": {
"gtags": {
"command": "uvx",
"args": ["mcp-gtags-server", "--root", "/绝对/路径/到/你的/项目"]
}
}
}
```
</details>
<details>
<summary><b>固定项目根目录</b> 显式</summary>
默认情况下,服务器使用客户端广告的工作区根目录(MCP 根目录协议)或通过从其工作目录向上遍历到最近的 `.git` 或存在的 `GTAGS` 自动检测项目根目录 — 因此来自 monorepo 的查询解析到仓库根目录。使用 `--root /path`、`GTAGS_MCP_ROOT` 环境变量或配置文件中的 `root` — 或在任何单独的工具调用上传递 `project_root` 以查询不同的树。(参见 ["一个配置,多个仓库"](https://github.com/harshithsunku/mcp-gtags-server/#one-config-many-repos) 以获取完整的解析顺序。)
</details>
<details>
<summary><b>配置文件</b> — 每个项目和每个用户默认值</summary>
每个设置也可以生活在 TOML 文件中,因此团队通过仓库共享默认值(如 `.editorconfig`):
- **项目**: `.gtags-mcp.toml` 在项目根目录
- **用户**: `~/.config/gtags-mcp/config.toml`
```toml
# .gtags-mcp.toml
label = "native-pygments" # 强制 GTAGSLABEL 解析器标签
bin_dir = "/opt/tools/bin" # 搜索 gtags/global/ctags 的额外目录
skip_globs = ["*.gen.c"] # 从不索引路径/ basename 匹配这些 glob
respect_gitignore = true # 默认:仅索引 `git ls-files` 报告的内容
enrich = true # 默认:结果中的 ctags kind/signature/scope
guards = true # 默认:结果中的 #ifdef 保护堆栈
macro_resolve = true # 默认:解析宏生成的符号(sys_*, ...)
# root = "/abs/path" # 默认项目根目录(用户配置)
```
优先级:工具调用参数 > CLI 标志 > 环境变量 > 项目配置 > 用户配置 > 内置默认值。
</details>
## 工具
### 符号级工具 — 噪音消除器
**给代理符号,不要文件。**
| 工具 | 代理获取的内容 |
|---|---|
| `symbol_info` | **一次性概览卡** — 定义(带有种类、签名、作用域和 `#ifdef` 保护)、引用计数、最热文件、`EXPORT_SYMBOL*` 状态以及下一个要使用的工具。多个定义的符号解释为“N 个定义在 M 个不同保护下”;宏生成的符号解析带有 `resolved_via` 标志。任何不熟悉的符号的最佳第一个查询。 |
| `get_symbol_body` | **仅定义源。** 271 行 `tcp_v4_rcv` 函数 — 而不是它所在的 3500 行文件。处理函数、结构体和多行宏。 |
| `find_callers` | **去重的调用图。** 每个引用映射到其封闭函数并带有调用计数:245 行原始行 `ext4_mark_inode_dirty` 折叠为 62 个调用者。迭代它以按需深入调用者图。 |
| `find_callees` | **输出调用图。** 调用了什么?提取的调用站点,每个通过索引验证,分为树内(带有位置)和外部。 |
| `reachability` | **“这个函数最终可以进入那个吗?”** — BFS 调用者图返回 A 到 B 的**最短** 调用链,带有每个调用站点的文件:行 (`ksys_read → vfs_read → rw_verify_area`) 或诚实的“没有静态路径” 名称函数指针的警告。一次调用代替十二轮 find_callers 轮。 |
| `blast_radius` | **我的 diff 影响什么?** 使用 `git diff <ref>`,将更改的行映射到其封闭函数索引,然后按距离排名 — 结果按距离排序(更改的函数优先,直接调用者) |
| `summarize_references` | **排名的文件计数。** 便宜的第一个动作 — `kmalloc` 的 2744 个引用变成一屏“哪里使用集中”。 |
### 核心查找
| 工具 | 它做什么 | 底层命令 |
|---|---|---|
| `find_definition` | 这个符号在哪里定义? 回落到宏系列解析(`sys_*`、`trace_*`、`DEFINE_*` 名称)当没有字面定义时,建议前缀匹配时全部匹配 | `global -x` + [宏解析](#宏生成的符号解析为 sys_read--syscall_define3) |
| `find_references` | 符号的原始引用行 — 如果索引没有树内引用,则回落到符号使用记录(libc 调用,一些变量)并标记 `fallback` 标志 | `global -rx`,备用 `global -sx` |
| `list_file_symbols` | 一个文件的 API 表面 — 该文件定义的每个符号 | `global -fx` |
| `update_index` | 编辑后的同步新鲜度屏障;`full=true` 从头开始重建(很少需要 — 索引是自动的) | `gtags -i` / `gtags` |
每个查询工具都支持 `limit`/`offset` 分页、长行截断和(在有意义的情况下)`case_insensitive` — 输出**经过工程设计**,绝不会淹没上下文窗口。
### 结构化输出(默认 JSON)
自 v0.8.0 起,每个工具默认返回一个**机器可读的 JSON 信封**(传递 `format="text"` 以获取以前的人类可读渲染 - 如果您解析了旧文本,则是一个重大更改):
```json
{
"tool": "find_definition",
"root": "/abs/project/root",
"results": [
{"symbol": "kmap", "path": "include/linux/highmem-internal.h", "line": 40,
"col": 22, "kind": "function", "typeref": "void *", "scope": null,
"signature": "(struct page * page)", "guard": ["CONFIG_HIGHMEM"],
"snippet": "static inline void *kmap(struct page *page)"}
],
"total": 2, "offset": 0, "truncated": false,
"next_tools": ["get_symbol_body", "find_callers", "symbol_info"],
"warning": null
}
```
- 符号位置始终使用稳定的记录模式 `{symbol, path, line, col, kind, typeref, scope, signature, guard, snippet}`,路径相对于存储库。键只会添加,绝不会重命名或删除 - 解析器永远不需要更改形状。
- **`kind` / `typeref` / `scope` / `signature` 说明符号是什么**(自 v0.8.1 起):函数 vs. 宏 vs. 结构体 vs. 类型定义 vs. 枚举常量,其返回/目标类型,其封闭范围(`enum:color`,`struct:item`)及其参数列表 - 由 universal-ctags 提取,每个文件无需构建和编译数据库,缓存后填充在定义形结果(`find_definition`,`symbol_info`,`list_file_symbols`)中。当 universal-ctags 不可用时,字段默认为 `null`;通过 `--no-enrich`、`GTAGS_MCP_ENRICH=0` 或 `.gtags-mcp.toml` 中的 `enrich = false` 显式禁用。
- **`guard` 说明符号何时存在**(自 v0.9.0 起):封闭的 `#if`/`#ifdef` 堆栈,从外到内(`[]` = 无条件,`null` = 扫描禁用或文件不可读)。参见下一节 - 这是主要功能。
- **`resolved_via` 说明符号如何被找到**,当它采用宏族解析而非字面索引匹配时(`"macro:SYSCALL_DEFINE"`,`"fuzzy:vfs_read"`)- 参见[宏生成的符号](#宏生成的符号也会被解析-sys_read-→-syscall_define3)。
- `next_tools` 告诉代理下一步调用的最高价值后续调用。
- `total`/`offset`/`truncated` 替换文本继续页脚;错误保留信封中的 `error` 字段。
- 复合工具返回工具形结果(例如 `find_callees` `{in_tree, external}`,`symbol_info` 概述对象,`reachability` 跳链)。
### 了解 #ifdef:知道您的配置实际编译的内容
内核和固件代码多次定义相同的符号,并让构建配置选择一个。其他所有不构建的工具都返回一个扁平的、未解释的列表 - 代理可以读取 `kmap` 的 no-op 存根并推导出错误的答案。此服务器读取预处理器条件(纯扫描 - 仍然无需构建,无 `compile_commands.json`):
```text
Symbol: kmap
2 definitions under 2 distinct guards:
[CONFIG_HIGHMEM] defined at include/linux/highmem-internal.h:40 — function kmap(struct page * page) -> void *
[!CONFIG_HIGHMEM] defined at include/linux/highmem-internal.h:170 — function kmap(struct page * page) -> void *
```
传递 `active_config` - 内核 `.config` 路径或宏列表,如 `"CONFIG_SMP,BITS_PER_LONG=64,!CONFIG_DEBUG"` - 到 `find_definition`、`find_references` 或 `symbol_info`,并且在它下面确定为假的 guard 堆栈的定义被丢弃(信封报告计数为 `config_filtered`)。过滤被故意保守:`.config` 是 `CONFIG_*` 宏的封闭世界(kbuild 语义,包括 `=m` → `CONFIG_X_MODULE` 和 `IS_ENABLED`/`IS_BUILTIN`/`IS_MODULE`),但任何未知的内容(`__ASSEMBLY__`、`ARCH_HAS_*`,它无法确定的算术)永远不会丢弃结果。
细节被处理以保持输出清洁:经典的 include guards(`#ifndef FOO_H`)被检测并抑制,`#elif` 链组成显式条件(`!CONFIG_X86_64 && CONFIG_X86_32`),指令上的注释被忽略(它们撒谎),而损坏/部分文件永远不会使查询失败。通过 `--no-guards`、`GTAGS_MCP_GUARDS=0` 或 `.gtags-mcp.toml` 中的 `guards = false` 禁用。
### 宏生成的符号也解析(`sys_read` → `SYSCALL_DEFINE3`)
内核中每个标记工具的最大已知空白:`sys_read`、`trace_sched_switch` 和 `css_set_lock` **在任何地方都没有字面定义** - 它们由 token-pasting 宏生成,而普通的查找返回空(或更糟糕的是,返回同名的测试助手)。此服务器仅从索引中解析它们,无需预处理器和构建:
```text
find_definition("sys_read") → fs/read_write.c:723 SYSCALL_DEFINE3(read, ...) resolved_via: "macro:SYSCALL_DEFINE"
find_definition("__x64_sys_openat")→ fs/open.c:1385 SYSCALL_DEFINE4(openat, ...) (arch entry-point wrappers map back)
find_definition("trace_sched_switch") → include/trace/events/sched.h:220 TRACE_EVENT(sched_switch, ...)
find_definition("css_set_lock") → kernel/cgroup/cgroup.c:82 DEFINE_SPINLOCK(css_set_lock);
```
涵盖的家族:`SYSCALL_DEFINE0..6` / `COMPAT_SYSCALL_DEFINE*`(包括 `__x64_`/`__ia32_`/`__arm64_`/`__se_`/`__do_` 包装器拼写),tracepoints(`TRACE_EVENT`、`DEFINE_EVENT`、`DECLARE_TRACE`,...),以及裸名定义(`DEFINE_SPINLOCK`、`DEFINE_MUTEX`、`DEFINE_PER_CPU*`、`DECLARE_BITMAP`、`module_param*`,任何 `DEFINE_`/`DECLARE_` 形状的宏)- 以及最后的模糊层,尝试下划线变体拼写。已解析的结果在信封中标记为 `resolved_via`,并优先于同名文本阴影;`DEFINE_*` 站点优先于其 `DECLARE_*` 对应项。`symbol_info` 还报告内核符号导出的 `EXPORT_SYMBOL` / `EXPORT_SYMBOL_GPL` 变体,在 `exported` 字段中。当符号正常解析时,成本为零(仅限 `sys_*`/`trace_*` 等家族名称会进行额外的索引查找);通过 `--no-macro-resolve`、`GTAGS_MCP_MACRO_RESOLVE=0` 或 `macro_resolve = false` 禁用。
### 索引内容(垃圾不会进入)
索引为 `gtags` 提供显式的文件列表,而不是让它遍历树:
- 在 git 仓库中,列表来自 `git ls-files` - **.gitignore 被精确尊重**,因此构建输出、供应商 blob 和生成的文件永远不会污染索引。通过 `.gtags-mcp.toml` 中的 `respect_gitignore = false` 禁用。
- 在 git 之外,遍历树时减去已知的垃圾目录(`.git`、`node_modules`、`build`、`dist`、`.venv`,...)。
- `.gtags-mcp.toml` 中的 `skip_globs` 删除您永远不想索引的任何其他内容。
增量刷新会重新收集列表,因此新忽略的文件会从索引中删除,新文件会出现 - 自动。
### 保留上下文窗口的流程
```text
1. symbol_info("kmalloc") → definitions + usage spread + next step (12 lines)
2. find_callers("ext4_mark_inode_dirty") → deduped callers with counts (1 line/caller)
3. get_symbol_body("tcp_v4_rcv") → read the ONE function that matters
4. find_callees("tcp_v4_rcv") → what it depends on, with locations
5. reachability("ksys_read", "rw_verify_area") → the call chain, one line per hop
6. blast_radius("HEAD") → what my edit impacts, ranked by distance
```
总共几百行上下文 - 相对于 grep-and-read-files 等价的数万行。
## 多语言项目(C + Python + 更多)
现实项目混合语言 - C 核心和 Python 工具,JS 前端,Go 服务。服务器自动处理:
- **原生语言**(C,C ++,Java,PHP,Yacc,汇编)使用 GNU Global 的快速内置解析器。
- **其他所有语言**(Python,Go,Rust,JavaScript,TypeScript,Ruby,... 约 150 种语言)通过 Global 的 **ctags + Pygments 插件解析器**进行索引 - 相同的索引,相同的工具,相同的查询。
单行安装程序(和 `mcp-gtags-server setup`)自动启用此功能 - 它在用户空间安装 universal-ctags 和 Pygments,服务器在自己的 `native-pygments` 解析器标签上切换。更喜欢系统软件包?那些也有效:
```bash
sudo apt install exuberant-ctags python3-pygments # Debian/Ubuntu
sudo dnf install ctags python3-pygments # Fedora
brew install ctags && pip install pygments # macOS
```
现在 `find_definition("py_util")`、`get_symbol_body`(Python 具有缩进感知)、`find_callees`、`find_callers` - 所有这些都在树中的每种语言上工作,在一个索引中。
通过 `--label`、`GTAGS_MCP_LABEL` 或 `.gtags-mcp.toml` 中的 `label` 强制使用特定的解析器标签(例如 `default` 表示仅限原生,`pygments` 表示所有插件)。
**诚实的警告:** 对于插件解析的语言,*定义* 的准确性与 ctags 一样,但 *引用* 基于标记 - 每个名称的出现都被计算在内,而不跟踪 C 级语义引用或局部范围感知。对于 C/C++,没有任何变化:本地解析器仍然负责那部分。
## 工作原理
### 架构
每个 IDE 窗口(stdio)或一台机器的共享 HTTP 服务器有一个长寿命的服务器进程。所有繁重的操作都存储在磁盘上并共享:工具链自行安装一次 per 台机器,索引自行构建一次 per 仓库。
```mermaid
flowchart LR
subgraph clients["MCP 客户端"]
CC["Claude Code / Cursor / VS Code"]
CD["Claude Desktop (.mcpb)"]
end
subgraph proc["mcp-gtags-server — 每窗口启动一次,持续到会话结束"]
T["11 种导航工具"]
RR["根解析<br/>project_root → env → config → client roots → cwd"]
end
subgraph disk["您的机器,用户空间(无需 sudo)"]
TC["~/.gtags-mcp<br/>global · gtags · ctags · Pygments<br/>自行安装(首次使用时)"]
IX["<repo>/.gtags-mcp/GTAGS<br/>每个仓库一个索引,自动构建"]
end
CC -- "stdio (uvx)" --> T
CD -- "stdio" --> T
T --> RR
RR -- "运行 global (毫秒,索引)" --> TC
TC -- "B-tree 查找" --> IX
```
### 工具调用,端到端
二进制文件**不会**每次调用都重新执行 - 服务器进程保持活动;每次调用都是一个 JSON-RPC 消息和一个毫秒级的 `global` 子进程:
```mermaid
sequenceDiagram
participant A as 代理
participant S as 服务器(长寿命进程)
participant G as global(GTAGS 索引)
A->>S: find_definition("tcp_v4_rcv")
Note over S: 这是这台机器上的第一次调用吗?<br/>工具链在后台自行安装,<br/>调用回答“安装中 - 请稍后重试”
Note over S: 仓库中的第一个查询?<br/>索引自动构建(~66 秒内核)
S->>G: global -dx tcp_v4_rcv
G-->>S: net/ipv4/tcp_ipv4.c:2067 (毫秒)
S-->>A: 狭窄的 JSON 答案 - 没有 grep 洪流
```
- **树上的第一个查询?** 索引会自动构建(唯一一个会阻塞的操作 - 并且仅一次)。
- **索引文件放在哪里?** 进入项目根目录下的单个 `.gtags-mcp/` 文件夹 - 您的代码旁边永远不会有松散文件。该文件夹自带 `.gitignore`,因此 `git status` 保持干净,无需触摸您现有的文件。预先存在的根级 `GTAGS`(来自旧版本,或您自己的 gtags 运行)将继续按原样使用。`mcp-gtags-server doctor` 显示位置。
- **文件更改?** 后台运行带消抖的增量刷新:查询始终从当前索引中立即回答,而 `gtags -i` 在后台赶上。通过 `update_index` 调用同步、保证新鲜的屏障在编辑后立即执行。
- **结果太大?** 分页页脚准确告诉代理如何获取下一页 - 或工具本身建议一个更窄的分页(`find_callers` 在 500 多个文件中使用符号会指向 `summarize_references`)。
## 常见问题
**为什么使用 gtags 而不是语言服务器(LSP)?**
LSP 服务器提供更丰富的语义,但需要有效的构建配置、每个编辑器的设置以及大型树上的大量预热时间。gtags 在约一分钟内索引 37M 行,**无需配置**,处理 LSP 无法处理的内核规模代码库,其模糊解析器不关心代码当前是否编译。对于 C/C++ 导航问题 - 定义、引用、调用者 - 它是务实的选择。(为 compile-DB 情况包装 clangd 被考虑并被故意拒绝:用户已经拥有 clangd 及其生态系统 - 参见 [ROADMAP.md](ROADMAP.md)。)
**支持哪些语言?**
原生支持 C、C++、Yacc、Java、PHP 和汇编语言 - 以及通过 ctags/Pygments 插件解析器支持 Python、Go、Rust、JS/TS、Ruby 和其他约 150 种语言(参见 [多语言项目](#多语言项目-c--python--更多))。
**代理必须管理索引吗?**
否。这就是重点。按需构建、后台刷新和自适应消抖 - 查询永远不会等待索引维护。显式 `update_index` 工具仅作为逃生阀:编辑后的同步新鲜度屏障和 `full=true` 的从头重建。
**索引存储在哪里?可以删除吗?**
在项目根目录下的 `.gtags-mcp/`(自 gitignore)。随时删除它 - 下一次查询会从头重建它。
**它会与我的代理内置工具冲突吗?**
工具描述旨在引导模型:它们说明**何时**使用索引查找而不是 grep。在实践中,代理会自然选择更快、更狭窄的工具。
## 开发
```bash
git clone https://github.com/harshithsunku/mcp-gtags-server
cd mcp-gtags-server
uv run --extra dev pytest # 229 个测试;如果没有 GNU Global,e2e 测试会自动跳过
npx @modelcontextprotocol/inspector mcp-gtags-server # 交互式探索
```
测试构建一个真实的 C 项目在临时目录中,并锻炼自动索引、自动刷新、调用者映射、主体提取、分页、用户空间二进制发现和配置分层端到端。
正确性也被衡量,而不是被假设:`mcp-gtags-server eval --golden evals/golden.jsonl --root <kernel-tree>` 在真正的内核上运行 65 个 golden 测试集,涵盖所有 11 种工具(定义、宏解析、引用、调用者、被调用者、主体、guard、可达性、维护)并打印召回率/精度@1 - CI 每周在固定标签上执行此操作。参见 [docs/capability.md](docs/capability.md) 获取当前数字。
发布流程:增加 `pyproject.toml` 中的 `version`,标记 `vX.Y.Z`,推送 - CI 发布到 PyPI,用户在下一次安装程序运行时获取更新。预构建的 GNU Global 二进制文件通过标记 `global-v<version>`(或 `gh workflow run release-binaries.yml -f version=<version>` 以原位替换现有版本的资产)重建;Linux 构建在许多 `manylinux_2_28` 容器中运行,因此它们在任何 ≥ 2.28 的 glibc 主机上工作,由 CI 符号上限检查强制执行。
## 路线图
参见 [ROADMAP.md](ROADMAP.md) - 结构化 JSON 输出于 v0.8.0 交付,ctags 元数据丰富(种类/签名/范围)于 v0.8.1,`#ifdef`/配置保护意识(内核和固件树的主要功能)于 v0.9.0,然后在 v1.0.0 中交付:宏族符号解析(`sys_read` → 其 `SYSCALL_DEFINE3` 站点),代理工作流工具(`reachability`、`blast_radius`),从损坏的索引数据库中自动恢复,以及正确性评估工具 - 65 个 golden 测试集对抗固定的内核 v6.16 得分 100% 召回率 / 100% 精确度@1,[docs/capability.md](docs/capability.md) 中有测量结果。
欢迎贡献 - 打开问题或 PR。
## 许可证
[MIT](LICENSE) 哈里特·孙库(Harshith Sunku)
MCP 配置
以下是此 MCP Server 的配置信息,可直接复制到 Cursor 或其他 MCP 客户端中使用。
mcp.json
连接信息
猜你喜欢
暂无推荐服务器