mcp-gtags-server 0.6.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mcp_gtags_server-0.6.0/.github/workflows/ci.yml +29 -0
- mcp_gtags_server-0.6.0/.github/workflows/publish.yml +63 -0
- mcp_gtags_server-0.6.0/.github/workflows/release-binaries.yml +122 -0
- mcp_gtags_server-0.6.0/.gitignore +20 -0
- mcp_gtags_server-0.6.0/LICENSE +21 -0
- mcp_gtags_server-0.6.0/PKG-INFO +342 -0
- mcp_gtags_server-0.6.0/README.md +294 -0
- mcp_gtags_server-0.6.0/pyproject.toml +56 -0
- mcp_gtags_server-0.6.0/scripts/benchmark.sh +47 -0
- mcp_gtags_server-0.6.0/scripts/install.sh +148 -0
- mcp_gtags_server-0.6.0/src/gtags_mcp/__init__.py +3 -0
- mcp_gtags_server-0.6.0/src/gtags_mcp/config.py +102 -0
- mcp_gtags_server-0.6.0/src/gtags_mcp/server.py +1297 -0
- mcp_gtags_server-0.6.0/src/gtags_mcp/toolchain.py +563 -0
- mcp_gtags_server-0.6.0/tests/test_config.py +75 -0
- mcp_gtags_server-0.6.0/tests/test_server.py +449 -0
- mcp_gtags_server-0.6.0/tests/test_toolchain.py +169 -0
- mcp_gtags_server-0.6.0/uv.lock +1035 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
strategy:
|
|
12
|
+
matrix:
|
|
13
|
+
python-version: ["3.10", "3.13"]
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
|
|
17
|
+
- name: Install GNU Global (for end-to-end tests)
|
|
18
|
+
run: sudo apt-get update && sudo apt-get install -y global exuberant-ctags python3-pygments
|
|
19
|
+
|
|
20
|
+
- name: Install uv
|
|
21
|
+
uses: astral-sh/setup-uv@v5
|
|
22
|
+
with:
|
|
23
|
+
python-version: ${{ matrix.python-version }}
|
|
24
|
+
|
|
25
|
+
- name: Run tests
|
|
26
|
+
run: uv run --extra dev pytest -v
|
|
27
|
+
|
|
28
|
+
- name: Shellcheck installer
|
|
29
|
+
run: bash -n scripts/install.sh
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
# Release flow (the installer's update channel):
|
|
4
|
+
# 1. Bump `version` in pyproject.toml on main.
|
|
5
|
+
# 2. Tag it: git tag v0.6.0 && git push origin v0.6.0
|
|
6
|
+
# 3. This workflow builds and publishes to PyPI and creates the GitHub
|
|
7
|
+
# release. Every user re-running install.sh (or `uv tool install
|
|
8
|
+
# --upgrade mcp-gtags-server`) then picks the update up automatically.
|
|
9
|
+
#
|
|
10
|
+
# Requires PyPI *trusted publishing* configured once for this repository:
|
|
11
|
+
# https://docs.pypi.org/trusted-publishers/ (project: mcp-gtags-server,
|
|
12
|
+
# workflow: publish.yml, environment: pypi).
|
|
13
|
+
|
|
14
|
+
on:
|
|
15
|
+
push:
|
|
16
|
+
tags:
|
|
17
|
+
- "v*"
|
|
18
|
+
|
|
19
|
+
jobs:
|
|
20
|
+
build:
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@v4
|
|
24
|
+
|
|
25
|
+
- name: Install uv
|
|
26
|
+
uses: astral-sh/setup-uv@v5
|
|
27
|
+
|
|
28
|
+
- name: Run tests first
|
|
29
|
+
run: |
|
|
30
|
+
sudo apt-get update && sudo apt-get install -y global
|
|
31
|
+
uv run --extra dev pytest -q
|
|
32
|
+
|
|
33
|
+
- name: Build sdist and wheel
|
|
34
|
+
run: uv build
|
|
35
|
+
|
|
36
|
+
- uses: actions/upload-artifact@v4
|
|
37
|
+
with:
|
|
38
|
+
name: dist
|
|
39
|
+
path: dist/
|
|
40
|
+
|
|
41
|
+
publish:
|
|
42
|
+
needs: build
|
|
43
|
+
runs-on: ubuntu-latest
|
|
44
|
+
environment: pypi
|
|
45
|
+
permissions:
|
|
46
|
+
id-token: write # PyPI trusted publishing
|
|
47
|
+
contents: write # GitHub release
|
|
48
|
+
steps:
|
|
49
|
+
- uses: actions/download-artifact@v4
|
|
50
|
+
with:
|
|
51
|
+
name: dist
|
|
52
|
+
path: dist/
|
|
53
|
+
|
|
54
|
+
- name: Publish to PyPI
|
|
55
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
56
|
+
|
|
57
|
+
- name: Create GitHub release
|
|
58
|
+
env:
|
|
59
|
+
GH_TOKEN: ${{ github.token }}
|
|
60
|
+
run: |
|
|
61
|
+
gh release create "$GITHUB_REF_NAME" --repo "$GITHUB_REPOSITORY" \
|
|
62
|
+
--title "mcp-gtags-server $GITHUB_REF_NAME" \
|
|
63
|
+
--generate-notes dist/*
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
name: Build prebuilt GNU Global binaries
|
|
2
|
+
|
|
3
|
+
# Builds relocatable GNU Global binaries with a long placeholder prefix that
|
|
4
|
+
# `gtags-mcp setup` patches to the user's ~/.gtags-mcp (conda-style
|
|
5
|
+
# relocation). Assets are attached to the release for tag `global-v<version>`
|
|
6
|
+
# and picked up automatically by setup on every user machine.
|
|
7
|
+
#
|
|
8
|
+
# Trigger by pushing a tag like `global-v6.6.15`, or manually.
|
|
9
|
+
|
|
10
|
+
on:
|
|
11
|
+
push:
|
|
12
|
+
tags:
|
|
13
|
+
- "global-v*"
|
|
14
|
+
workflow_dispatch:
|
|
15
|
+
inputs:
|
|
16
|
+
version:
|
|
17
|
+
description: "GNU Global version to build (e.g. 6.6.15)"
|
|
18
|
+
required: true
|
|
19
|
+
default: "6.6.15"
|
|
20
|
+
|
|
21
|
+
permissions:
|
|
22
|
+
contents: write
|
|
23
|
+
|
|
24
|
+
env:
|
|
25
|
+
GLOBAL_SHA256: cf0937cb3ed521b2ab1acfa7aff45103040b860bb642c4c2f094ac3a3fe86024
|
|
26
|
+
|
|
27
|
+
jobs:
|
|
28
|
+
build:
|
|
29
|
+
strategy:
|
|
30
|
+
matrix:
|
|
31
|
+
include:
|
|
32
|
+
- runner: ubuntu-22.04
|
|
33
|
+
platform: linux-x86_64
|
|
34
|
+
- runner: ubuntu-22.04-arm
|
|
35
|
+
platform: linux-aarch64
|
|
36
|
+
- runner: macos-13
|
|
37
|
+
platform: macos-x86_64
|
|
38
|
+
- runner: macos-latest
|
|
39
|
+
platform: macos-aarch64
|
|
40
|
+
runs-on: ${{ matrix.runner }}
|
|
41
|
+
steps:
|
|
42
|
+
- name: Resolve version
|
|
43
|
+
id: ver
|
|
44
|
+
run: |
|
|
45
|
+
if [[ "${GITHUB_REF_TYPE}" == "tag" ]]; then
|
|
46
|
+
echo "version=${GITHUB_REF_NAME#global-v}" >> "$GITHUB_OUTPUT"
|
|
47
|
+
else
|
|
48
|
+
echo "version=${{ github.event.inputs.version }}" >> "$GITHUB_OUTPUT"
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
- name: Download and verify source
|
|
52
|
+
run: |
|
|
53
|
+
curl -fsSLO "https://ftp.gnu.org/gnu/global/global-${{ steps.ver.outputs.version }}.tar.gz"
|
|
54
|
+
echo "${GLOBAL_SHA256} global-${{ steps.ver.outputs.version }}.tar.gz" | shasum -a 256 -c -
|
|
55
|
+
tar xzf "global-${{ steps.ver.outputs.version }}.tar.gz"
|
|
56
|
+
|
|
57
|
+
- name: Build with placeholder prefix
|
|
58
|
+
run: |
|
|
59
|
+
# Must match PLACEHOLDER_PREFIX in src/gtags_mcp/toolchain.py:
|
|
60
|
+
# "/opt/gtags-mcp-placeholder-prefix" + 167 underscores (200 chars).
|
|
61
|
+
PREFIX="/opt/gtags-mcp-placeholder-prefix$(printf '_%.0s' $(seq 1 167))"
|
|
62
|
+
sudo mkdir -p "$PREFIX"
|
|
63
|
+
sudo chown "$(id -u)" "$PREFIX"
|
|
64
|
+
cd "global-${{ steps.ver.outputs.version }}"
|
|
65
|
+
./configure --prefix="$PREFIX" --disable-gtagscscope
|
|
66
|
+
make -j"$(getconf _NPROCESSORS_ONLN)"
|
|
67
|
+
make install
|
|
68
|
+
echo "PREFIX=$PREFIX" >> "$GITHUB_ENV"
|
|
69
|
+
|
|
70
|
+
- name: Smoke test
|
|
71
|
+
run: |
|
|
72
|
+
mkdir smoke && cd smoke
|
|
73
|
+
printf 'int foo(int x){return x;}\nint main(void){return foo(1);}\n' > a.c
|
|
74
|
+
"$PREFIX/bin/gtags"
|
|
75
|
+
"$PREFIX/bin/global" -x foo | grep -q a.c
|
|
76
|
+
|
|
77
|
+
- name: Package
|
|
78
|
+
run: |
|
|
79
|
+
STAGE="global-${{ steps.ver.outputs.version }}-${{ matrix.platform }}"
|
|
80
|
+
mkdir "$STAGE"
|
|
81
|
+
cp -R "$PREFIX/bin" "$PREFIX/lib" "$PREFIX/share" "$STAGE/"
|
|
82
|
+
tar czf "$STAGE.tar.gz" "$STAGE"
|
|
83
|
+
shasum -a 256 "$STAGE.tar.gz"
|
|
84
|
+
|
|
85
|
+
- name: Upload artifact
|
|
86
|
+
uses: actions/upload-artifact@v4
|
|
87
|
+
with:
|
|
88
|
+
name: global-${{ steps.ver.outputs.version }}-${{ matrix.platform }}
|
|
89
|
+
path: global-${{ steps.ver.outputs.version }}-${{ matrix.platform }}.tar.gz
|
|
90
|
+
|
|
91
|
+
release:
|
|
92
|
+
needs: build
|
|
93
|
+
runs-on: ubuntu-latest
|
|
94
|
+
steps:
|
|
95
|
+
- name: Resolve version
|
|
96
|
+
id: ver
|
|
97
|
+
run: |
|
|
98
|
+
if [[ "${GITHUB_REF_TYPE}" == "tag" ]]; then
|
|
99
|
+
echo "version=${GITHUB_REF_NAME#global-v}" >> "$GITHUB_OUTPUT"
|
|
100
|
+
else
|
|
101
|
+
echo "version=${{ github.event.inputs.version }}" >> "$GITHUB_OUTPUT"
|
|
102
|
+
fi
|
|
103
|
+
|
|
104
|
+
- name: Download all artifacts
|
|
105
|
+
uses: actions/download-artifact@v4
|
|
106
|
+
with:
|
|
107
|
+
merge-multiple: true
|
|
108
|
+
|
|
109
|
+
- name: Generate checksums
|
|
110
|
+
run: sha256sum global-*.tar.gz > checksums.txt && cat checksums.txt
|
|
111
|
+
|
|
112
|
+
- name: Create or update release
|
|
113
|
+
env:
|
|
114
|
+
GH_TOKEN: ${{ github.token }}
|
|
115
|
+
run: |
|
|
116
|
+
TAG="global-v${{ steps.ver.outputs.version }}"
|
|
117
|
+
gh release view "$TAG" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1 || \
|
|
118
|
+
gh release create "$TAG" --repo "$GITHUB_REPOSITORY" \
|
|
119
|
+
--title "GNU Global ${{ steps.ver.outputs.version }} prebuilt binaries" \
|
|
120
|
+
--notes "Relocatable user-space GNU Global binaries consumed by \`gtags-mcp setup\`. Not for direct use."
|
|
121
|
+
gh release upload "$TAG" --repo "$GITHUB_REPOSITORY" --clobber \
|
|
122
|
+
global-*.tar.gz checksums.txt
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Harshith Sunku
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mcp-gtags-server
|
|
3
|
+
Version: 0.6.0
|
|
4
|
+
Summary: Indexed code navigation for AI coding agents β replace grep scans with GNU Global (gtags) lookups over MCP. ~100x faster and radically less noise on million-line C/C++ codebases.
|
|
5
|
+
Project-URL: Homepage, https://github.com/harshithsunku/mcp-gtags-server
|
|
6
|
+
Project-URL: Repository, https://github.com/harshithsunku/mcp-gtags-server
|
|
7
|
+
Project-URL: Issues, https://github.com/harshithsunku/mcp-gtags-server/issues
|
|
8
|
+
Author: Harshith Sunku
|
|
9
|
+
License: MIT License
|
|
10
|
+
|
|
11
|
+
Copyright (c) 2026 Harshith Sunku
|
|
12
|
+
|
|
13
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
14
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
15
|
+
in the Software without restriction, including without limitation the rights
|
|
16
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
17
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
18
|
+
furnished to do so, subject to the following conditions:
|
|
19
|
+
|
|
20
|
+
The above copyright notice and this permission notice shall be included in all
|
|
21
|
+
copies or substantial portions of the Software.
|
|
22
|
+
|
|
23
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
24
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
25
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
26
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
27
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
28
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
29
|
+
SOFTWARE.
|
|
30
|
+
License-File: LICENSE
|
|
31
|
+
Keywords: ai-agents,c,c++,claude,code-navigation,code-search,gnu-global,gtags,mcp,model-context-protocol
|
|
32
|
+
Classifier: Development Status :: 4 - Beta
|
|
33
|
+
Classifier: Intended Audience :: Developers
|
|
34
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
35
|
+
Classifier: Operating System :: OS Independent
|
|
36
|
+
Classifier: Programming Language :: Python :: 3
|
|
37
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
38
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
39
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
40
|
+
Classifier: Topic :: Software Development
|
|
41
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
42
|
+
Requires-Python: >=3.10
|
|
43
|
+
Requires-Dist: mcp>=1.8.0
|
|
44
|
+
Requires-Dist: tomli>=2.0; python_version < '3.11'
|
|
45
|
+
Provides-Extra: dev
|
|
46
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
47
|
+
Description-Content-Type: text/markdown
|
|
48
|
+
|
|
49
|
+
# β‘ mcp-gtags-server
|
|
50
|
+
|
|
51
|
+
> **Stop letting your AI agent grep. Give it an index.**
|
|
52
|
+
|
|
53
|
+
[](https://www.python.org/downloads/)
|
|
54
|
+
[](LICENSE)
|
|
55
|
+
[](https://modelcontextprotocol.io/)
|
|
56
|
+
[](https://www.gnu.org/software/global/)
|
|
57
|
+
|
|
58
|
+
Every AI coding agent β Claude Code, Cursor, Codex, you name it β answers *"where is this function defined?"* the same way: **grep the entire tree**. On a million-line C/C++ codebase that's a full scan per question, and the output is a firehose: every comment, string literal, and unrelated match, dumped straight into the model's context window.
|
|
59
|
+
|
|
60
|
+
**mcp-gtags-server** replaces those scans with indexed lookups powered by [GNU Global (gtags)](https://www.gnu.org/software/global/) β the same tags engine kernel and systems developers have trusted for decades β exposed to agents over the [Model Context Protocol](https://modelcontextprotocol.io/).
|
|
61
|
+
|
|
62
|
+
- π **~100Γ faster per query** β milliseconds instead of seconds, at any codebase size
|
|
63
|
+
- π― **Radically less noise** β the definition, not 7,873 lines of matches
|
|
64
|
+
- π§ **Zero index management** β first query builds the index, every query auto-refreshes it
|
|
65
|
+
- π **Works everywhere MCP does** β Claude Code, Claude Desktop, Cursor, any MCP client
|
|
66
|
+
|
|
67
|
+
## π The numbers (real Linux kernel, not a toy)
|
|
68
|
+
|
|
69
|
+
Measured on a full Linux kernel checkout β **65,163 C/C++ files, 37.1 million lines** β warm page cache:
|
|
70
|
+
|
|
71
|
+
| Question an agent asks | `grep -rn` | gtags (this server) | Context consumed |
|
|
72
|
+
|---|---|---|---|
|
|
73
|
+
| Where is `tcp_v4_rcv` defined? | 1.40 s | **0.01 s** | 8 lines β **1 line** |
|
|
74
|
+
| Where is `kmalloc` defined? | 1.62 s | **0.01 s** | 7,873 lines β **5 lines** |
|
|
75
|
+
| Who references `kmalloc`? | 1.62 s | **0.10 s** | 7,873 noisy lines β 2,744 real sites (or a **ranked per-file summary**) |
|
|
76
|
+
| Show me `tcp_v4_rcv`'s implementation | *read a 3,500-line file* | **`get_symbol_body`** | **exactly the 271-line function** |
|
|
77
|
+
| Who calls `ext4_mark_inode_dirty`? | 245 raw match lines | **`find_callers`** | **62 deduped caller functions, with counts** |
|
|
78
|
+
|
|
79
|
+
One-time index build: **66 s** for the whole kernel. Incremental refresh after edits: well under a second. Reproduce it yourself with [`scripts/benchmark.sh`](scripts/benchmark.sh):
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
./scripts/benchmark.sh /path/to/linux tcp_v4_rcv kmalloc ext4_readdir
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
The speed is nice. The real win is **precision**: an agent that gets 5 exact lines instead of 7,873 noisy ones keeps its context window for actual reasoning.
|
|
86
|
+
|
|
87
|
+
## π Quick start (60 seconds)
|
|
88
|
+
|
|
89
|
+
**One command. No sudo. Works everywhere** β restricted corporate machines, containers, build servers:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
curl -fsSL https://raw.githubusercontent.com/harshithsunku/mcp-gtags-server/main/scripts/install.sh | bash
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Everything lands in your home directory β the server (via `uv`), GNU Global, universal-ctags, and Pygments (in `~/.gtags-mcp`). When it finishes, an MCP server is **already running in the background** and the exact client configuration is printed to your console:
|
|
96
|
+
|
|
97
|
+
```text
|
|
98
|
+
==> All set! Connect your tools with the configuration below:
|
|
99
|
+
|
|
100
|
+
MCP client configuration (HTTP transport):
|
|
101
|
+
|
|
102
|
+
Claude Code (once per device, all repos):
|
|
103
|
+
claude mcp add --scope user --transport http gtags http://127.0.0.1:8383/mcp
|
|
104
|
+
|
|
105
|
+
Cursor / any MCP client β global settings or .mcp.json:
|
|
106
|
+
{
|
|
107
|
+
"mcpServers": {
|
|
108
|
+
"gtags": { "url": "http://127.0.0.1:8383/mcp" }
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
**Re-run the same command any time:**
|
|
114
|
+
|
|
115
|
+
- Up to date? β *"Already installed and up to date β nothing to install"*, and the config is printed again.
|
|
116
|
+
- New release on GitHub/PyPI? β the package updates, an outdated gtags toolchain is wiped and reinstalled automatically, and the background server restarts on the new version.
|
|
117
|
+
|
|
118
|
+
Prefer stdio (client-launched processes) over a background server? That works too β `GTAGS_MCP_NO_SERVER=1` skips the server, and any client can use:
|
|
119
|
+
|
|
120
|
+
```json
|
|
121
|
+
{
|
|
122
|
+
"mcpServers": {
|
|
123
|
+
"gtags": {
|
|
124
|
+
"command": "gtags-mcp"
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
That's it. No indexing step, no configuration, **no per-repo setup** β 20 repos need zero extra installs. Ask your agent *"who calls `tcp_v4_rcv`?"* β the first query in any repo builds that repo's index automatically, and every query after that is answered in milliseconds. Run `gtags-mcp doctor` any time to see what the server detects, or `gtags-mcp config` to re-print the client configuration.
|
|
131
|
+
|
|
132
|
+
<details>
|
|
133
|
+
<summary><b>Background server details</b> (network access, port, lifecycle)</summary>
|
|
134
|
+
|
|
135
|
+
The installer runs `gtags-mcp --transport http --host 127.0.0.1 --port 8383` in the background (pid: `~/.gtags-mcp/server.pid`, log: `~/.gtags-mcp/server.log`). Environment overrides for the installer:
|
|
136
|
+
|
|
137
|
+
| Variable | Default | Meaning |
|
|
138
|
+
|---|---|---|
|
|
139
|
+
| `GTAGS_MCP_PORT` | `8383` | HTTP port |
|
|
140
|
+
| `GTAGS_MCP_HOST` | `127.0.0.1` | Bind address β set `0.0.0.0` to reach the server from other devices at `http://<machine-ip>:8383/mcp` |
|
|
141
|
+
| `GTAGS_MCP_NO_SERVER` | unset | `1` = don't start a background server |
|
|
142
|
+
|
|
143
|
+
**Security note:** the HTTP endpoint is unauthenticated. It binds localhost by default; only bind `0.0.0.0` on networks you trust.
|
|
144
|
+
|
|
145
|
+
</details>
|
|
146
|
+
|
|
147
|
+
<details>
|
|
148
|
+
<summary><b>Manual install</b> (prefer system packages, or already have Global)</summary>
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
# 1. GNU Global β EITHER user-space (no sudo):
|
|
152
|
+
gtags-mcp setup
|
|
153
|
+
# OR a system package:
|
|
154
|
+
sudo apt install global # Debian/Ubuntu
|
|
155
|
+
sudo dnf install global # Fedora
|
|
156
|
+
brew install global # macOS
|
|
157
|
+
|
|
158
|
+
# 2. The server:
|
|
159
|
+
uv tool install mcp-gtags-server # or: pip install mcp-gtags-server
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
The server finds binaries in this order: `--bin-dir`/`GTAGS_MCP_BIN_DIR`/config `bin_dir` β `~/.gtags-mcp/bin` β `PATH` β `~/.local/bin`.
|
|
163
|
+
|
|
164
|
+
</details>
|
|
165
|
+
|
|
166
|
+
<details>
|
|
167
|
+
<summary><b>Claude Desktop</b> config</summary>
|
|
168
|
+
|
|
169
|
+
Add to `claude_desktop_config.json` (pin the project since Desktop doesn't launch in your repo):
|
|
170
|
+
|
|
171
|
+
```json
|
|
172
|
+
{
|
|
173
|
+
"mcpServers": {
|
|
174
|
+
"gtags": {
|
|
175
|
+
"command": "gtags-mcp",
|
|
176
|
+
"args": ["--root", "/absolute/path/to/your/project"]
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
</details>
|
|
183
|
+
|
|
184
|
+
<details>
|
|
185
|
+
<summary><b>Pin a project root</b> explicitly</summary>
|
|
186
|
+
|
|
187
|
+
The default project root is the server's working directory. Override with `--root /path`, the `GTAGS_MCP_ROOT` env var, or `root` in a config file β or pass `project_root` on any individual tool call to query a different tree.
|
|
188
|
+
|
|
189
|
+
</details>
|
|
190
|
+
|
|
191
|
+
<details>
|
|
192
|
+
<summary><b>Config files</b> β per-project and per-user defaults</summary>
|
|
193
|
+
|
|
194
|
+
Every setting can also live in a TOML file, so teams share defaults through the repo (like `.editorconfig`):
|
|
195
|
+
|
|
196
|
+
- **Project**: `.gtags-mcp.toml` at the project root
|
|
197
|
+
- **User**: `~/.config/gtags-mcp/config.toml`
|
|
198
|
+
|
|
199
|
+
```toml
|
|
200
|
+
# .gtags-mcp.toml
|
|
201
|
+
label = "native-pygments" # force a GTAGSLABEL parser label
|
|
202
|
+
bin_dir = "/opt/tools/bin" # extra directory searched for gtags/global/ctags
|
|
203
|
+
# root = "/abs/path" # default project root (user config)
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Precedence: tool-call argument > CLI flag > environment variable > project config > user config > built-in default.
|
|
207
|
+
|
|
208
|
+
</details>
|
|
209
|
+
|
|
210
|
+
## π§° The tools
|
|
211
|
+
|
|
212
|
+
### Symbol-level tools β the noise killers
|
|
213
|
+
|
|
214
|
+
The pattern proven out by semantic-code servers like [Serena](https://github.com/oraios/serena): **give the agent the symbol, not the file.**
|
|
215
|
+
|
|
216
|
+
| Tool | What the agent gets |
|
|
217
|
+
|---|---|
|
|
218
|
+
| `symbol_info` | **A one-shot overview card** β definitions, reference count, hottest files, and which tool to use next. The best first query for any unfamiliar symbol. |
|
|
219
|
+
| `get_symbol_body` | **Just the source of a definition.** The 271-line `tcp_v4_rcv` function β not the 3,500-line file it lives in. Handles functions, structs, and multi-line macros. |
|
|
220
|
+
| `find_callers` | **The call graph, deduplicated.** Every reference mapped to its enclosing function with call counts: 245 raw lines for `ext4_mark_inode_dirty` collapse to 62 callers. |
|
|
221
|
+
| `call_hierarchy` | **Multi-level impact analysis.** Who calls X, who calls *those*, up to 5 levels β a cycle-safe, capped tree instead of N rounds of grep. |
|
|
222
|
+
| `find_callees` | **The outgoing call graph.** What does this function call? Body-extracted call sites, each verified against the index, split into in-tree (with locations) and external. |
|
|
223
|
+
| `summarize_references` | **A ranked per-file count.** The cheap first move for hot symbols β `kmalloc`'s 2,744 references become one screen of "where usage concentrates". |
|
|
224
|
+
| `project_overview` | **Orientation in an unfamiliar repo** β file counts by top-level directory and language, straight from the index. |
|
|
225
|
+
| `find_dead_symbols` | **Dead-code candidates** β every symbol a file defines that nothing references. |
|
|
226
|
+
| `find_includers` | **Header blast radius** β every file that `#include`s a header, matched by basename. |
|
|
227
|
+
|
|
228
|
+
A two-level `call_hierarchy` on the kernel's `ext4_mark_inode_dirty` β 87 compact lines instead of dozens of grep rounds:
|
|
229
|
+
|
|
230
|
+
```text
|
|
231
|
+
ext4_mark_inode_dirty (definition: fs/ext4/ext4_jbd2.h:138)
|
|
232
|
+
ββ ext4_rename fs/ext4/namei.c (6 sites)
|
|
233
|
+
β ββ ext4_rename2 fs/ext4/namei.c (1 site)
|
|
234
|
+
ββ swap_inode_boot_loader fs/ext4/ioctl.c (5 sites)
|
|
235
|
+
β ββ __ext4_ioctl fs/ext4/ioctl.c (1 site)
|
|
236
|
+
ββ ext4_mkdir fs/ext4/namei.c (3 sites)
|
|
237
|
+
β ββ ext4_rename2 fs/ext4/namei.c (1 site)
|
|
238
|
+
...
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### Core lookups
|
|
242
|
+
|
|
243
|
+
| Tool | What it does | Underlying command |
|
|
244
|
+
|---|---|---|
|
|
245
|
+
| `find_definition` | Where is this symbol defined? | `global -x` |
|
|
246
|
+
| `find_references` | Raw reference lines for a symbol | `global -rx` |
|
|
247
|
+
| `find_symbol_usages` | Usages of symbols with no in-tree definition (libc calls etc.) | `global -sx` |
|
|
248
|
+
| `grep_project` | Regex search across indexed files | `global -gx` |
|
|
249
|
+
| `list_file_symbols` | A file's API surface β every symbol it defines | `global -fx` |
|
|
250
|
+
| `complete_symbol` | Symbols starting with a prefix | `global -c` |
|
|
251
|
+
| `find_files` | Indexed files whose path matches a regex | `global -P` |
|
|
252
|
+
| `index_project` / `update_index` | Force rebuild / refresh (rarely needed β it's automatic) | `gtags` / `global -u` |
|
|
253
|
+
|
|
254
|
+
Every query tool supports `limit`/`offset` pagination with a continuation footer, long-line truncation, and (where it makes sense) `case_insensitive` β output is *engineered* to never flood a context window.
|
|
255
|
+
|
|
256
|
+
### The flow that saves your context window
|
|
257
|
+
|
|
258
|
+
```text
|
|
259
|
+
0. project_overview() β orient in an unfamiliar repo (12 lines)
|
|
260
|
+
1. symbol_info("kmalloc") β definitions + usage spread + next step (12 lines)
|
|
261
|
+
2. call_hierarchy("ext4_mark_inode_dirty") β multi-level impact tree (1 line/caller)
|
|
262
|
+
3. get_symbol_body("tcp_v4_rcv") β read the ONE function that matters
|
|
263
|
+
4. find_callees("tcp_v4_rcv") β what it depends on, with locations
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
A few hundred lines of context total β versus tens of thousands for the grep-and-read-files equivalent.
|
|
267
|
+
|
|
268
|
+
## π Multi-language projects (C + Python + more)
|
|
269
|
+
|
|
270
|
+
Real projects mix languages β a C core with Python tooling, JS frontends, Go services. The server handles this automatically:
|
|
271
|
+
|
|
272
|
+
- **Native languages** (C, C++, Java, PHP, Yacc, assembly) use GNU Global's fast built-in parser.
|
|
273
|
+
- **Everything else** (Python, Go, Rust, JavaScript, TypeScript, Ruby, ... ~150 languages) is indexed through Global's **ctags + Pygments plugin parsers** β same index, same tools, same queries.
|
|
274
|
+
|
|
275
|
+
The one-line installer (and `gtags-mcp setup`) enables this automatically β it installs universal-ctags and Pygments into user space, and the server switches to the `native-pygments` parser label on its own. Prefer system packages? Those work too:
|
|
276
|
+
|
|
277
|
+
```bash
|
|
278
|
+
sudo apt install exuberant-ctags python3-pygments # Debian/Ubuntu
|
|
279
|
+
sudo dnf install ctags python3-pygments # Fedora
|
|
280
|
+
brew install ctags && pip install pygments # macOS
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
Now `find_definition("py_util")`, `get_symbol_body` (indentation-aware for Python), `find_callees`, `call_hierarchy` β all work across every language in the tree, in one index.
|
|
284
|
+
|
|
285
|
+
Force a specific parser label with `--label`, `GTAGS_MCP_LABEL`, or `label` in `.gtags-mcp.toml` (e.g. `default` for native-only, `pygments` for plugin-everything).
|
|
286
|
+
|
|
287
|
+
**Honest caveats:** for plugin-parsed languages, *definitions* are as accurate as ctags, but *references* are token-based β every occurrence of the name counts, without C-grade semantic reference tracking or local-scope awareness. For C/C++ nothing changes: the native parser still does that part.
|
|
288
|
+
|
|
289
|
+
## βοΈ How it works
|
|
290
|
+
|
|
291
|
+
```text
|
|
292
|
+
agent question βββΊ MCP tool βββΊ GTAGS index (built once, ~66s for the kernel)
|
|
293
|
+
β
|
|
294
|
+
background auto-refresh (global -u, adaptive debounce)
|
|
295
|
+
β
|
|
296
|
+
narrow answer βββΊ agent context
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
- **First query on a tree?** The index is built automatically (the only operation that ever blocks β and only once).
|
|
300
|
+
- **Files changed?** A debounced incremental refresh runs **in the background**: queries always answer instantly from the current index while `global -u` catches up behind the scenes. Measured on the kernel: queries return in 0.02s while the 25s freshness check runs invisibly. Staleness is bounded by the debounce window; call `update_index` for a synchronous, guaranteed-fresh barrier right after edits.
|
|
301
|
+
- **Huge result?** Pagination footers tell the agent exactly how to fetch the next page β or the tool itself suggests a narrower one (`find_callers` on a symbol used in 500+ files points to `summarize_references`).
|
|
302
|
+
|
|
303
|
+
## β FAQ
|
|
304
|
+
|
|
305
|
+
**Why gtags instead of a language server (LSP)?**
|
|
306
|
+
LSP servers give richer semantics but need a working build configuration, per-editor setup, and serious warm-up time on large trees. gtags indexes 37M lines in about a minute with *zero* configuration, handles the kernel-scale codebases LSPs choke on, and its fuzzy parser doesn't care whether the code currently compiles. For C/C++ navigation questions β definition, references, callers β it's the pragmatic sweet spot.
|
|
307
|
+
|
|
308
|
+
**What languages?**
|
|
309
|
+
C, C++, Yacc, Java, PHP, and assembly natively β plus Python, Go, Rust, JS/TS, Ruby, and ~150 others via the ctags/Pygments plugin parsers (see [Multi-language projects](#-multi-language-projects-c--python--more)).
|
|
310
|
+
|
|
311
|
+
**Does the agent have to manage the index?**
|
|
312
|
+
No. That's the point. Build-on-first-query, background refresh with adaptive debounce, zero blocking β queries never wait for index maintenance. The explicit `index_project`/`update_index` tools exist only as escape hatches (`update_index` doubles as a synchronous freshness barrier after edits).
|
|
313
|
+
|
|
314
|
+
**Will it fight my agent's built-in tools?**
|
|
315
|
+
The tool descriptions are written to steer the model: they say *when* to use indexed lookups instead of grep. In practice agents pick the faster, narrower tool naturally.
|
|
316
|
+
|
|
317
|
+
## π οΈ Development
|
|
318
|
+
|
|
319
|
+
```bash
|
|
320
|
+
git clone https://github.com/harshithsunku/mcp-gtags-server
|
|
321
|
+
cd mcp-gtags-server
|
|
322
|
+
uv venv && uv pip install -e ".[dev]"
|
|
323
|
+
pytest # 21 tests; auto-skip if GNU Global is absent
|
|
324
|
+
npx @modelcontextprotocol/inspector gtags-mcp # poke at it interactively
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
Tests build a real C project in a temp dir and exercise auto-indexing, auto-refresh, caller mapping, body extraction, and pagination end-to-end.
|
|
328
|
+
|
|
329
|
+
## πΊοΈ Roadmap
|
|
330
|
+
|
|
331
|
+
- [x] Multi-level call hierarchy (`call_hierarchy`, depth 1β5) for transitive impact analysis
|
|
332
|
+
- [x] Outgoing call graph (`find_callees`), symbol overview cards (`symbol_info`), project orientation (`project_overview`)
|
|
333
|
+
- [x] Dead-code candidates (`find_dead_symbols`) and header blast radius (`find_includers`)
|
|
334
|
+
- [x] Multi-language projects (Python, Go, Rust, JS, ... via ctags/Pygments plugin parsers, auto-detected)
|
|
335
|
+
- [ ] Structured (JSON) result variants for machine-readable output
|
|
336
|
+
- [ ] Published benchmarks vs LSP-based MCP servers
|
|
337
|
+
|
|
338
|
+
Contributions welcome β open an issue or PR.
|
|
339
|
+
|
|
340
|
+
## π License
|
|
341
|
+
|
|
342
|
+
[MIT](LICENSE) Β© Harshith Sunku
|