关于 mcp-gtags-server
mcp-gtags-server 通过 MCP 向编码 Agent 提供基于 GNU Global 索引的 C/C++ 代码导航,适用于大型或难以构建的代码库。它可查找符号、引用、调用者和调用链,避免宽泛的文本扫描。
主要功能
- 01索引式符号定义与引用查询
- 02调用者、被调用者及调用链查询
- 03获取函数体
- 04解析内核宏与导出符号
- 05自动创建并刷新索引
- 06跨代码库选择项目根目录
适用场景
- 01在大型 C/C++ 代码树中查找符号
- 02修改函数前追踪其调用者
- 03无需读取大文件即可查看函数体
- 04将引用搜索限制在项目路径内
MCP 相关
向 MCP 客户端提供八种代码导航工具,以及用于影响分析和代码解释的提示词。可通过 stdio 或共享本地 HTTP 服务运行。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
uvx mcp-gtags-serverGTAGS_MCP_ROOTOptional pinned project root. Usually unnecessary: the server uses the client's workspace roots or auto-detects the repo from its working directory.GTAGS_MCP_AUTO_SETUPSet to 0 to disable the automatic user-space install of the gtags/ctags toolchain on first use.本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。
工具
8收录的工具列表 · 更新于 3 周前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| limit | integer | 否 | |
| format | enum | 否 | json | text |
| offset | integer | 否 | |
| symbol | string | 是 | |
| project_root | any | 否 | |
| active_config | any | 否 | |
| case_insensitive | boolean | 否 |
详细介绍
mcp-gtags-server
别再让 AI Agent 使用 grep。给它一个索引。
mcp-name: io.github.harshithsunku/mcp-gtags-server
# If you don't have uv yet (one-time, no sudo):
curl -LsSf https://astral.sh/uv/install.sh | sh
{ "mcpServers": { "gtags": { "command": "uvx", "args": ["mcp-gtags-server"] } } }
只需一条用户级配置,无需 sudo,也无需预先安装任何内容——整个工具链会在首次使用时自动安装到用户空间,并自动为你打开的每个代码仓库提供服务。
📖 完整文档 — 了解如何为每种客户端安装、工具参考、输出格式、内核功能、配置和故障排除。
每个 AI 编码 Agent——无论是 Claude Code、Cursor、Codex,还是其他工具——遇到 “这个函数定义在哪里?” 时,都会用同一种方式回答:对整个代码树运行 grep。在百万行规模的 C/C++ 代码库中,每个问题都要完整扫描一次,输出更是铺天盖地:每条注释、字符串字面量和无关匹配都会直接塞进模型的上下文窗口。
mcp-gtags-server 使用 GNU Global (gtags) 提供的索引查询取代这些扫描——这套标签引擎数十年来一直深受内核和系统开发者信赖——并通过 Model Context Protocol 向 Agent 提供服务。专为 LSP 工具难以应对的代码库打造:内核规模的 C/C++、当前无法编译的代码树,以及无法使用 sudo 的机器。
- 每次查询快约 100 倍 — 毫秒级而非秒级,无论代码库规模如何
- 噪声大幅减少 — 直接给出定义,而不是 7,873 行匹配结果
- 懂内核 — 支持结合
.config过滤#ifdef守卫栈;可解析其他标签工具无法识别的宏生成符号(sys_read→ 对应的SYSCALL_DEFINE3位置);还能通过 ctags,从EXPORT_SYMBOL位置找回解析器遗漏的定义 - 无需管理索引 — 首次查询时构建索引,每次查询都会自动刷新
- 在 CI 中验证正确性 — 针对固定版本内核,使用涵盖全部 8 个工具的 64 个用例进行黄金评测:召回率 100%,precision@1 为 100%(docs/capability.md)
- 适用于所有支持 MCP 的场景 — Claude Code、Claude Desktop、Cursor 以及任何 MCP 客户端
数字说话(真实的 Linux 内核,而非玩具项目)
在完整的 Linux 内核代码库上测得:65,163 个 C/C++ 文件,3,710 万行代码,热页缓存:
| Agent 会问的问题 | grep -rn | gtags(此服务) | 消耗的上下文 |
|---|---|---|---|
tcp_v4_rcv 定义在哪里? | 1.40 s | 0.01 s | 8 行 → 1 行 |
kmalloc 定义在哪里? | 1.62 s | 0.01 s | 7,873 行 → 5 行 |
谁引用了 kmalloc? | 1.62 s | 0.10 s | 7,873 行杂乱结果 → 2,744 个真实位置(或按文件排名的摘要) |
展示 tcp_v4_rcv 的实现 | 读取一个 3,500 行的文件 | get_symbol_body | 恰好是这个 271 行的函数 |
谁调用了 ext4_mark_inode_dirty? | 245 行原始匹配结果 | find_callers | 62 个去重后的调用方函数,附带计数 |
sys_read 实际 定义在哪里? | 无答案——名称由宏生成 | 0.03 s | fs/read_write.c SYSCALL_DEFINE3(read, ...),并标记 resolved_via |
mutex_lock 定义在哪里? | 24,774 行杂乱匹配结果 | 0.2 s | kernel/locking/mutex.c:314——gtags 的解析器在此处失效后,通过 EXPORT_SYMBOL + ctags 找回 |
ksys_read 是否会到达 rw_verify_area? | 多轮 grep 和阅读 | 0.6 s | 最短调用链,包含每个调用位置的文件和行号 |
fs/ext4 中的每个 mutex_lock 调用? | 整棵目录树共有 22,905 行匹配结果 | 0.01 s | find_references(path_prefix="fs/ext4"):18 个真实位置 |
内核的索引仅需构建一次:66 s。编辑后的增量刷新:远低于一秒。你可以使用 scripts/benchmark.sh 自行复现:
./scripts/benchmark.sh /path/to/linux tcp_v4_rcv kmalloc ext4_readdir
速度提升固然不错,真正的优势在于精准度:Agent 获取的是 5 行准确结果,而不是 7,873 行杂乱内容,因此可以将上下文窗口留给真正的推理。
这些结果来自实际测量,而非假设:每次推送时,CI 都会针对固定版本的内核 v6.16 运行 64 项黄金评估——目前,在定义、宏解析、导出恢复、引用、调用方、被调用方、定义函数体、#ifdef 守卫和可达性方面,召回率为 100%,Precision@1 为 100%,涵盖全部 8 个工具。完整的方法、数据和坦诚说明的局限性请参阅 docs/capability.md。
快速开始(60 秒)
选项 A — 添加一条配置(推荐)
在用户级别添加一次此服务即可——无需安装程序、预先安装 gtags,也无需针对每个代码库单独设置。唯一的前置条件是 uv(也可以使用会为你安装它的选项 B)。
# Claude Code (once per device, all repos):
claude mcp add --scope user gtags -- uvx mcp-gtags-server
// Cursor (~/.cursor/mcp.json), or any MCP client's global settings:
{
"mcpServers": {
"gtags": { "command": "uvx", "args": ["mcp-gtags-server"] }
}
}
也可以点击本页顶部的 Cursor 一键安装徽章。
首次调用工具时,服务会自动完成其余所有设置:GNU Global(预构建的用户空间二进制文件——无需编译器,也无需 sudo)、universal-ctags 和 Pygments,全部安装到 ~/.gtags-mcp。首次安装期间(大多数平台只需几秒),工具调用会返回 “toolchain is being installed — retry shortly” 状态,而不是失败。可使用 --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 — 安装插件(服务 + 技能 + 斜杠命令)
该插件捆绑了此服务、一个 “C/C++ 代码导航”技能(用于告知 Agent 哪个工具可以回答哪个问题),以及两个斜杠命令
(/gtags:impact、/gtags:explain)。安装后会为你注册 MCP 服务——无需编辑配置文件。
| 客户端 | 安装 |
|---|---|
| Claude Code | /plugin marketplace add harshithsunku/mcp-gtags-server,然后运行 /plugin install mcp-gtags-server@mcp-gtags-server |
| Codex | 运行 codex plugin marketplace add harshithsunku/mcp-gtags-server,然后在 ChatGPT 桌面应用的 Plugins Directory 中安装 |
| Cursor | 可加载 Agent Plugins 1.0.0,但只能从 Cursor Marketplace 或团队市场安装——在此插件上架之前,请使用一键徽章或选项 A |
选择一种安装方式,不要两种都用。 插件和手动条目同时启用会运行两个服务, Agent 会看到两份相同的工具。要切换到插件?请先移除手动条目(
claude mcp remove gtags)。
选项 C — 单行安装程序(共享后台服务)
一条命令。无需 sudo。随处可用——受限的企业设备、容器、构建服务:
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),并且确切的客户端配置会打印到控制台。
随时重新运行同一条命令:
- 已是最新版本?→ 显示 “Already installed and up to date — nothing to install”,并再次打印配置。
- GitHub/PyPI 上有新版本?→ 更新软件包,自动清除并重新安装过时的 gtags 工具链,然后以新版本重启后台服务。
斜杠命令(MCP 提示词)
可在支持 MCP 提示词的客户端(Claude Code、 Cursor)中配合任意安装方式使用。不会占用工具架构上下文:
| 命令 | 执行内容 |
|---|---|
/gtags:impact [git_ref] | git diff → 已更改的函数 → 对每个函数运行 find_callers → 按风险为调用方排序 |
/gtags:explain <symbol> | 定义(+ #ifdef 变体)→ 函数体 → 调用方 → 简短说明 |
一份配置,多个代码库
无论采用哪种安装方式,一个用户级条目即可服务你打开的所有代码库——20 个代码库无需额外安装,也无需额外配置。每次工具调用都会按以下顺序解析项目根目录:
- 工具调用中的
project_root参数(Agent 会传入此参数,以指定任意目录树) --root标志 /GTAGS_MCP_ROOT环境变量- 配置文件中的
root(注意:在此固定根目录会导致无法服务多个代码库) - 客户端的工作区根目录(MCP roots 协议)——声明了打开文件夹的 IDE 会自动选择正确的代码库,即使使用共享 HTTP 服务也一样;打开多个文件夹时,会要求 Agent 传入
project_root。Roots 仅适用于协议修订版本不高于 2025-11-25 的客户端:2026-07-28 版已弃用该功能,因此这些客户端会跳过此步骤(stdio 服务仍会通过第 5 步解析;使用共享 HTTP 服务时,Agent 会传入project_root) - 从服务的工作目录向上查找最近的
.git/GTAGS——因此,在代码库内由 Claude Code/Cursor 启动的 stdio 服务可以直接使用
可通过 --no-cwd-fallback / GTAGS_MCP_CWD_FALLBACK=0 关闭第 5 步
(插件会设置此项):随后,工具会要求传入 project_root,而不是索引服务恰好运行所在的目录。插件客户端会从插件自身目录启动
服务,而该目录永远不是你的项目目录。
就这样。无需索引步骤,也无需配置。向你的 Agent 提问 “谁调用了 tcp_v4_rcv?” ——在任何仓库中,首次查询都会自动为该仓库建立索引,此后的每次查询都能在几毫秒内得到回答。随时运行 mcp-gtags-server doctor,查看服务检测到的内容;或运行 mcp-gtags-server config,重新显示客户端配置。
后台服务详情(网络访问、端口、生命周期)
安装程序会在后台运行 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 端点未经身份验证。默认绑定到本地主机;仅在可信网络上绑定 0.0.0.0。
手动安装(优先使用系统软件包,或已安装 Global)
# 1. GNU Global — EITHER user-space (no sudo):
mcp-gtags-server setup
# OR a system package:
sudo apt install global # Debian/Ubuntu
sudo dnf install global # Fedora
brew install global # macOS
# 2. The server:
uv tool install mcp-gtags-server # or: pip install mcp-gtags-server
服务按以下顺序查找二进制文件:--bin-dir/GTAGS_MCP_BIN_DIR/配置中的 bin_dir → ~/.gtags-mcp/bin → PATH → ~/.local/bin。
Claude Desktop — 扩展包或手动配置
最简单的方式:从最新版本下载 mcp-gtags-server.mcpb,将其拖入 Claude Desktop 的 Settings → Extensions,然后在设置界面中选择项目文件夹。gtags 工具链会在首次使用时自动安装。
或者手动将配置添加到 claude_desktop_config.json(固定指定项目,因为 Desktop 启动时不会位于你的仓库中):
{
"mcpServers": {
"gtags": {
"command": "uvx",
"args": ["mcp-gtags-server", "--root", "/absolute/path/to/your/project"]
}
}
}
固定项目根目录(显式指定)
默认情况下,服务会使用客户端声明的工作区根目录(MCP roots 协议),或从其工作目录向上查找最近的 .git 或现有的 GTAGS,自动检测项目根目录——因此,在单体仓库内的任何位置发起查询,都会解析到仓库根目录。可通过 --root /path、GTAGS_MCP_ROOT 环境变量或配置文件中的 root 覆盖;也可在单次工具调用中传入 project_root,以查询其他目录树。(完整的解析顺序请参阅“一份配置,多个仓库”。)
配置文件 — 项目级和用户级默认值
所有设置也可以写入 TOML 文件,以便团队通过仓库共享默认设置(类似 .editorconfig):
- 项目级:项目根目录下的
.gtags-mcp.toml - 用户级:
~/.config/gtags-mcp/config.toml
# .gtags-mcp.toml
label = "native-pygments" # force a GTAGSLABEL parser label
bin_dir = "/opt/tools/bin" # extra directory searched for gtags/global/ctags
skip_globs = ["*.gen.c"] # never index paths/basenames matching these globs
respect_gitignore = true # default: index only what `git ls-files` reports
enrich = true # default: ctags kind/signature/scope on results
guards = true # default: #ifdef guard stacks on results
macro_resolve = true # default: resolve macro-generated symbols (sys_*, ...)
# root = "/abs/path" # default project root (user config)
优先级:工具调用参数 > CLI 标志 > 环境变量 > 项目配置 > 用户配置 > 内置默认值。
共有八个工具,除 update_index 外均为只读。每个工具都需要 project_root 和
format(默认为 "text",也可为 "json");列表类工具还接受 limit/offset。
完整参数参考:文档 → 工具。
- find_definition — 跳转到定义:返回每个定义及其
#ifdef保护条件栈、ctags 类型/签名,以及用法摘要(引用数/文件数、引用最多的文件、EXPORT_SYMBOL*状态)。可解析宏生成的名称(sys_read→SYSCALL_DEFINE3)。 - find_references — 返回每个使用位置及其保护条件栈;引用数超过 200 时,返回按文件统计的分布,
path_prefix可将范围缩小到一个目录。 - get_symbol_body — 返回一个定义(函数、结构体或多行宏)的源码,而非整个文件。
- find_callers — 谁调用了这个函数:将引用映射到其所属函数,去重后提供调用次数和调用行号。
- find_callees — 这个函数调用了什么:提取函数体中的调用点并与索引核对,分为树内(含位置)和外部调用。
- reachability — 函数 A 到函数 B 的最短静态调用链,包含每一跳的 file:line;如果不存在,则返回“无静态路径”。
- list_file_symbols — 返回文件定义的每个符号(其 API 面)。
- update_index — 编辑后的同步索引刷新;
full=true会从头重建。唯一会写入的工具。
符号级工具——减少噪声
给 Agent 符号,而不是文件。
| 工具 | Agent 获取的内容 |
|---|---|
get_symbol_body | 仅返回定义的源码。 271 行的 tcp_v4_rcv 函数——而不是它所在的 3,500 行文件。支持函数、结构体和多行宏。 |
find_callers | 去重后的调用图。 将每个引用映射到其所属函数并统计调用次数:ext4_mark_inode_dirty 的 245 行原始结果收敛为 62 个调用方。可逐层遍历,按需深入调用方图。 |
find_callees | 向外的调用图。 这个函数调用了什么?从函数体提取调用点,并逐一与索引核对,分为树内(含位置)和外部调用。 |
reachability | “这个函数能否最终调用到那个函数?” — 在调用方图上进行 BFS,返回 A 到 B 的最短调用链,并包含每个调用点的 file:line(ksys_read → vfs_read → rw_verify_area);如果不存在,则如实返回“无静态路径”,并说明函数指针的限制。一次调用即可替代十几轮 find_callers。 |
核心查询
| 工具 | 功能 | 底层命令 |
|---|---|---|
find_definition | 定义在哪里?它是什么? 显示带有 #ifdef 守卫和 ctags 元数据的定义,以及使用情况摘要(引用数/文件数、最常涉及的文件、EXPORT_SYMBOL* 状态)。未找到时,会回退到宏族解析(sys_*、trace_*、DEFINE_* 名称),并建议前缀匹配项 | global -x + 宏生成的符号也能解析:sys_read、SYSCALL_DEFINE3 |
find_references | 列出每个使用位置及其守卫栈。引用超过 200 个时,会返回按文件分布而非长串行(kmalloc 的 2,744 个引用只需一屏);path_prefix 可深入指定目录,group_by 可强制选择其中一种视图。未找到时,会回退到符号使用记录(libc 调用、部分变量),并标记为 fallback | global -rx -S <dir>,回退命令为 global -sx |
list_file_symbols | 文件的 API 面 — 列出它定义的所有符号 | global -fx |
update_index | 编辑后同步刷新索引;full=true 会从头重建(很少需要 — 索引会自动更新) | gtags -i / gtags |
每个查询工具都支持 limit/offset 分页、长行截断,以及(适用时)case_insensitive — 输出经过专门设计,不会让上下文窗口被内容淹没。
每个工具还声明了 MCP 工具注解:除 update_index 外,其他工具的 readOnlyHint 均为 true;没有任何工具会访问你机器之外的内容(openWorldHint: false)。客户端会利用这些提示自动批准只读调用,并行执行它们。每个响应的封装内容只会以文本内容的形式出现一次,不会重复提供 structuredContent 副本。
节省上下文窗口的工作流
1. find_definition("kmalloc") → definitions + #ifdef variants + usage spread (3 lines)
2. find_callers("ext4_mark_inode_dirty") → deduped callers, each with its call line
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. find_references("mutex_lock", path_prefix="fs/ext4") → hot symbol, one subsystem
总共只需几百行上下文 — 而 grep 并读取文件的等效操作需要数万行。
输出与内核智能
输出:默认采用紧凑文本,按需使用 JSON
从 v2.0.0 起,每个工具都会以 grep 风格的文本作答——这是 Agent 最易读取的格式,与相同答案的 JSON 格式相比,可节省 2–3 倍的令牌:
kmap: 4 definitions under 3 #if variants · 261 refs in 65 files (top: tools/perf/util/machine.c 26, …)
include/linux/highmem-internal.h:40: static inline void *kmap(struct page *page) [function; #if CONFIG_HIGHMEM]
include/linux/highmem-internal.h:170: static inline void *kmap(struct page *page) [function; #if !CONFIG_HIGHMEM]
next: get_symbol_body, find_callers, find_references
path:line: source 行会带有标签,说明符号的类型,以及它所在的 #if/#ifdef 堆栈。分页结果末尾会显示类似 [1-100 of 5290 · offset=100 for more] 的页脚。
与 v1.x 默认使用 JSON 相比,同样的七个内核问题总体可节省 20% 的令牌,单次查询最高可节省 68%。只有 find_callers 这次调用变得 更大(约 9%),这是有意为之:它现在会包含每个调用位置的源代码行,而 Agent 以前需要额外执行一次 grep 才能看到这一信息。
传入 format="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,
"definition_count": 2, "guard_variants": 2, "reference_count": 261,
"file_count": 65, "top_files": [{"path": "…", "count": 26}], "exported": null,
"next_tools": ["get_symbol_body", "find_callers", "find_references"],
"warning": null
}
- 符号位置始终使用稳定的记录架构
{symbol, path, line, col, kind, typeref, scope, signature, guard, snippet},路径相对于仓库。键只会新增,在同一主版本中不会重命名或移除。 kind/typeref/scope/signature说明符号是什么(从 v0.8.1 起):它是函数、宏、结构体、typedef 还是枚举常量;其返回类型/目标类型、所属作用域(enum:color、struct:item)及参数列表——由 universal-ctags 逐文件提取,无需构建,也无需编译数据库,并会缓存;定义形式的结果(find_definition、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会告知 Agent 针对此次找到(或未找到)的内容,最有价值的后续调用是什么。total/offset/truncated用于分页;失败时仍会保留封装格式并包含error字段,同时会在协议中标记isError,以便 Agent 自行纠正。- 组合工具会在同一封装格式中返回工具形式的
results(例如,find_callees返回{in_tree, external},reachability返回跳数链)。 - 每个工具都声明了 MCP 工具注解:除
update_index外,所有工具的readOnlyHint: true,且没有任何工具会访问本机以外的内容(openWorldHint: false)——客户端可据此自动批准调用并并行执行。
从 v1.x 升级? 在任何解析旧版默认格式的地方显式传入 format="json"。symbol_info 现在已并入 find_definition,summarize_references 更名为 find_references(会自动按文件分组),而 blast_radius 对应 /gtags:impact 提示词——执行 git diff,然后对每个变更函数调用 find_callers。
#ifdef 感知:了解配置实际编译的是哪个定义
内核和固件代码会多次定义同一个符号,并让构建配置来选择其中一个。其他所有无需构建的工具都会返回一份扁平且没有说明的列表——Agent 会欣然读取 kmap 的空操作存根,并据此推理出错误答案。此服务会读取预处理器条件(纯扫描——仍然无需构建,也不需要 compile_commands.json):
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,即可过滤掉其守卫条件栈在该配置下确定为假的定义(信封会通过 config_filtered 报告过滤数量)。过滤策略有意保持保守:对于 CONFIG_* 宏,.config 是一个封闭世界(遵循 kbuild 语义,包括 =m → CONFIG_X_MODULE 以及 IS_ENABLED/IS_BUILTIN/IS_MODULE),但对于任何未知项(__ASSEMBLY__、ARCH_HAS_*、无法判定的算术表达式),绝不会过滤结果。
为保持输出整洁,相关细节也已处理:经典包含守卫(#ifndef FOO_H)会被检测并抑制,#elif 链会组合成显式条件(!CONFIG_X86_64 && CONFIG_X86_32),指令中的注释会被忽略(它们会误导),损坏或不完整的文件也不会导致查询失败。可通过 .gtags-mcp.toml 中的 --no-guards、GTAGS_MCP_GUARDS=0 或 guards = false 禁用此功能。
宏生成的符号也能解析(sys_read → SYSCALL_DEFINE3)
内核中所有标签工具最广为人知的不足是:sys_read、trace_sched_switch 和 css_set_lock 在任何地方都没有字面定义——它们由 token-pasting 宏生成,因此普通查找会返回空结果(更糟糕的是,还可能返回一个同名的测试辅助函数)。此服务仅凭索引即可解析这些符号,无需预处理器或构建:
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_ 包装器写法)、跟踪点(TRACE_EVENT、DEFINE_EVENT、DECLARE_TRACE 等),以及裸名称定义宏(DEFINE_SPINLOCK、DEFINE_MUTEX、DEFINE_PER_CPU*、DECLARE_BITMAP、module_param*,以及任何形如 DEFINE_/DECLARE_ 的宏)——此外还有最后兜底的模糊匹配层,会尝试下划线变体写法。解析出的结果会在信封中通过 resolved_via 标记,并排在同名文本匹配项之前;DEFINE_* 位置的排名高于对应的 DECLARE_* 位置。find_definition 还会通过 exported 字段报告内核符号使用了哪种 EXPORT_SYMBOL / EXPORT_SYMBOL_GPL 变体导出。符号能正常解析时不会产生额外开销(只有 sys_*/trace_* 等符合系列特征的名称才会执行额外的索引查找);可通过 --no-macro-resolve、GTAGS_MCP_MACRO_RESOLVE=0 或 macro_resolve = false 禁用。
索引内容(排除无用内容)
索引时会向 gtags 提供显式文件列表,而不是让它遍历整个目录树:
- 在 git 仓库中,文件列表来自
git ls-files——会严格遵循.gitignore,因此构建输出、供应商提供的文件块和生成文件都不会污染索引。可在.gtags-mcp.toml中设置respect_gitignore = false禁用。 - 在 git 仓库之外,会遍历目录树,但排除常见的无用目录(
.git、node_modules、build、dist、.venv等)。 .gtags-mcp.toml中的skip_globs可排除其他不希望编入索引的内容。
增量刷新时会重新收集文件列表,因此新加入忽略规则的文件会从索引中移除,新文件也会自动加入。
多语言项目(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 解析器标签。更喜欢使用系统软件包?也可以:
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 服务。所有耗费资源的内容都保存在磁盘上并共享:每台机器只需安装一次工具链,每个仓库只需构建一次索引。
flowchart LR
subgraph clients["MCP clients"]
CC["Claude Code / Cursor / VS Code"]
CD["Claude Desktop (.mcpb)"]
end
subgraph proc["mcp-gtags-server — spawned once per window, lives for the session"]
T["11 navigation tools"]
RR["root resolution<br/>project_root → env → config → client roots → cwd"]
end
subgraph disk["your machine, user space (no sudo)"]
TC["~/.gtags-mcp<br/>global · gtags · ctags · Pygments<br/>self-installs on first use"]
IX["<repo>/.gtags-mcp/GTAGS<br/>one index per repo, auto-built"]
end
CC -- "stdio (uvx)" --> T
CD -- "stdio" --> T
T --> RR
RR -- "runs global (ms, indexed)" --> TC
TC -- "B-tree lookup" --> IX
一次工具调用的完整过程
每次调用都不会重新执行二进制文件——服务进程会持续运行;每次调用都是一条 JSON-RPC 消息,加上一个耗时仅数毫秒的 global 子进程:
sequenceDiagram
participant A as Agent
participant S as server (long-lived process)
participant G as global (GTAGS index)
A->>S: find_definition("tcp_v4_rcv")
Note over S: first call on this machine?<br/>toolchain installs itself in the background,<br/>calls answer "installing — retry shortly"
Note over S: first query in this repo?<br/>index auto-builds once (~66 s for the kernel)
S->>G: global -dx tcp_v4_rcv
G-->>S: net/ipv4/tcp_ipv4.c:2067 (milliseconds)
S-->>A: narrow JSON answer — no grep firehose
- **第一次查询代码树?**索引会自动构建(这是唯一会阻塞的操作,而且只会发生一次)。
- **索引文件存放在哪里?**存放在项目根目录下单独的
.gtags-mcp/文件夹中——不会散落在代码旁。该文件夹自带.gitignore,因此无需修改你自己的文件,git status也能保持干净。已有的根目录级GTAGS(由旧版本或你自己运行 gtags 生成)会继续原样使用。mcp-gtags-server doctor会显示其位置。 - 文件有更改?系统会在后台执行防抖增量刷新:查询始终从当前索引即时返回,同时
gtags -i在后台追赶更新。在内核代码库上的测量结果显示,25 秒的索引新鲜度检查在后台运行时,查询仍能在 0.02 秒内返回。索引过期时间受防抖窗口限制;编辑后调用update_index,即可同步等待索引更新完成并确保索引最新。 - **结果过多?**分页页脚会准确告知 Agent 如何获取下一页;工具也可能直接建议使用范围更窄的工具(在 500 多个文件中使用的符号,可通过
find_callers定位到find_references,后者会按文件分组)。
常见问题
为什么用 gtags 而不是语言服务器(LSP)?
LSP 服务器能提供更丰富的语义,但需要可用的构建配置、针对每种编辑器进行设置,而且在大型代码树上预热耗时很长。gtags 无需任何配置,约一分钟即可为 3,700 万行代码建立索引;它能处理让 LSP 不堪重负的内核规模代码库,而且其模糊解析器不在乎代码当前能否编译。对于 C/C++ 导航问题——定义、引用、调用者——它是务实的理想选择。(曾考虑过针对有编译数据库的情况封装 clangd,但最终有意否决:拥有可用 compile_commands.json 的用户已经有 clangd 及其生态系统——参见 ROADMAP.md。)
支持哪些语言? 原生支持 C、C++、Yacc、Java、PHP 和汇编;此外,通过 ctags/Pygments 插件解析器支持 Python、Go、Rust、JS/TS、Ruby 及约 150 种其他语言(参见多语言项目)。
Agent 必须管理索引吗?
不用。这正是重点。首次查询时构建索引,后台刷新采用自适应防抖,完全不会阻塞——查询不会等待索引维护。显式的 update_index 工具只是一个应急选项:编辑后同步确保索引为最新,以及通过 full=true 从头重建索引。
索引存放在哪里?可以删除吗?
它位于项目根目录下的 .gtags-mcp/ 中(会自动加入 Git 忽略)。随时可以放心删除——下次查询时会从头重建索引。
它会和 Agent 内置工具冲突吗? 工具说明经过编写,旨在引导模型:说明何时应使用索引查找而不是 grep。实际上,Agent 自然会选择更快、范围更窄的工具。
启动时出现 ModuleNotFoundError: No module named 'mcp.server.fastmcp'?
截至 v1.4.2 的版本声明了未设上限的 mcp 依赖,因此在 MCP Python SDK 2.0 发布后安装的版本会解析到无法导入的 SDK。v1.4.3 通过将 SDK 版本限制在 2.0 以下修复了此问题;v1.5.0+ 原生运行于 SDK 2.x(mcp>=2.2,<3),支持从 2024-11-05 到 2026-07-28 的所有协议修订版。运行 uvx --refresh mcp-gtags-server --help(或 uvx mcp-gtags-server@latest)获取修复,或重新运行安装程序。如果必须使用较旧版本,请自行固定 SDK 版本:uvx --with 'mcp<2' [email protected]。
开发
git clone https://github.com/harshithsunku/mcp-gtags-server
cd mcp-gtags-server
uv run --extra dev pytest # 325 tests; e2e tests auto-skip if GNU Global is absent
npx @modelcontextprotocol/inspector mcp-gtags-server # poke at it interactively
测试会在临时目录中构建一个真实的 C 项目,并端到端测试自动建立索引、自动刷新、调用者映射、函数体提取、分页、用户空间二进制文件发现和配置分层。
正确性也会通过测量来验证,而非想当然:mcp-gtags-server eval --golden evals/golden.jsonl --root <kernel-tree> 会针对真实内核运行包含 64 个用例的黄金测试集,覆盖全部 8 个工具(定义、宏解析、引用、调用者、被调用者、函数体、保护条件、可达性、维护),并输出召回率 / precision@1——CI 每周会针对固定标签运行此测试。当前数据请参见 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 容器中运行,确保可在任何 glibc ≥ 2.28 的主机上使用,并由 CI 的符号版本上限检查强制保证。
路线图
请参阅 ROADMAP.md — 结构化 JSON 输出已于 v0.8.0 发布,ctags 元数据增强(kind/signature/scope)已于 v0.8.1 发布,#ifdef/配置守卫感知能力(内核和固件代码树的核心能力)已于 v0.9.0 发布,随后所有功能均已在 v1.0.0 发布:宏族符号解析(sys_read → 对应的 SYSCALL_DEFINE3 位置)、Agent 工作流工具(可达性、差异影响分析)、从损坏的索引数据库中自动恢复,以及正确性评估框架 — 在 CI 中使用一组针对固定内核 v6.16 的 64 个黄金测试用例,得分达到 100% 召回率 / 100% precision@1,测量结果详见 docs/capability.md。所有技术里程碑均已完成;剩下的工作是分发(MCP 注册表、目录和发布说明文章)。
欢迎贡献 — 请提交 issue 或 PR。
许可证
MIT © Harshith Sunku
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。