Scalable CTags

Máy chủ MCP CTags với tính năng tự động khám phá, được thiết kế cho các dự án lớn

Tài liệu

MCP server to allow for code navigation using a tags file. Aims to be able to handle largs tags files with minimal overhead.

Lars Hollenbach db4e896232 fix(server): register watcher with absolute path on manual load...

load_tags_file called o.watcher.Add(path) with the user-supplied path,
which can be relative. fsnotify reports events for that file under a
different key (the path the kernel gives back), so a relative-path load
could silently drop watcher events.

Use the absolute path returned by ParseTagsFile (pf.Path), which the index and discovery layer already canonicalise to. Add tests covering both plain and non-canonical (./tags) relative inputs.

AI-Assisted: written with opencode and MiniMax-M3

2026-08-05 04:00:18 +02:00
archived-planschore(plans): move auto-discovery to archive2026-07-14 22:34:30 +02:00
cmd/ctags-mcpfeat(server) Load discovered tags files in background worker pool2026-08-05 03:46:06 +02:00
internalfix(server): register watcher with absolute path on manual load2026-08-05 04:00:18 +02:00
testdata/tagsfeat: implement scalable CTags MCP server with memory-mapped file parsing2026-07-10 02:15:43 +02:00
.envrcfeat: implement scalable CTags MCP server with memory-mapped file parsing2026-07-10 02:15:43 +02:00
.gitignorechore: fix gitignore, add missing main.go2026-07-14 23:17:16 +02:00
AGENTS.mdfeat(server) Load discovered tags files in background worker pool2026-08-05 03:46:06 +02:00
flake.lockfeat: implement scalable CTags MCP server with memory-mapped file parsing2026-07-10 02:15:43 +02:00
flake.nixfeat: implement scalable CTags MCP server with memory-mapped file parsing2026-07-10 02:15:43 +02:00
go.modrefactor(parser,indexer,server) consolidate shared fields into CTagsEntry and strip filePaths + fileID from TagIndex2026-07-15 03:04:31 +02:00
go.sumrefactor(parser,indexer,server) consolidate shared fields into CTagsEntry and strip filePaths + fileID from TagIndex2026-07-15 03:04:31 +02:00
README.mdfeat(server) Load discovered tags files in background worker pool2026-08-05 03:46:06 +02:00

Scalable CTags MCP Server

A memory-mapped ctags symbol lookup server that exposes an MCP (Model Context Protocol) interface for IDE agents and LLM tooling. Designed to handle multiple and large ctags files across several subprojects.

Features

  • Multi-file support: Load and search across multiple tags files simultaneously
  • Automatic Refresh: Watches loaded files, refreshes automatically
  • Extended fields: Extracts kind, language, and filePath from ctags v2 extended format

Building

Requires Go 1.26+ and a Nix environment (or standard Go toolchain).

# With Nix (recommended)
nix develop --command go build -o ctags-mcp ./cmd/ctags-mcp

# Without Nix
go build -o ctags-mcp ./cmd/ctags-mcp

Tags file

Generate a tags file for your project before running the server:

ctags -n -R .

The -n flag (--excmd=number) is required — it instructs ctags to store line numbers instead of regex patterns as search commands. Without it, the server returns line: 0 for all symbol lookups.

Running

The server communicates via stdio (JSON-RPC).

./ctags-mcp

On startup, the server automatically discovers tags and tags.in files from the current working directory (up to 8 levels deep) and parses them in a background goroutine pool — the MCP handshake answers immediately, no matter how large the tags files are. Lookup tools (lookup_symbol, get_symbol_info) block until the initial load completes; list_tags_files answers right away and reports per-file status (loading / ready) plus a top-level loadingComplete flag. Hidden directories (dot-prefixed) are always skipped. Use --auto-discover=false to disable auto-discovery entirely.

By default additional directories matching the flag --skip-dirs (comma-separated dir names) are excluded (e.g. vendor, node_modules).

Configure discovery:

FlagDefaultDescription
--auto-discovertrueEnable (true) or disable (false) auto-discovery
--depth8Max recursion levels below the working directory
--skip-dirs(none)Comma-separated directory names to exclude

Using in opencode

Add the server to your opencode.json under the mcp key:

{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "ctags": {
      "type": "local",
      "command": ["./ctags-mcp"],
      "enabled": true
    }
  }
}

After editing opencode.json, quit and restart opencode for changes to take effect.

Available Tools

The load_tags_file response includes a preloaded field set to true when the file was auto-discovered at startup.

ToolDescriptionParameters
load_tags_fileLoad a ctags tags file and build the indexpath (required)
lookup_symbolSearch the index for a symbol by name (blocks until initial load completes)name (required), fileID (optional)
get_symbol_infoGet detailed info about a single symbol (blocks until initial load completes)name (required), fileID (optional)
list_tags_filesList all discovered tags files with metadata, status (loading / ready) and loadingComplete flagnone

Available Resources

URIDescription
tagsfile://metadataJSON metadata for a loaded tags file
file://{abspath}:{line}Source line content from a tag entry

Usage Example

Once the server is configured in opencode, you can ask natural language questions and the agent will use the tools:

"Find all references to AddTagsFile in the codebase"
"Show me detailed info about the EntryRef struct"
"What tags files are currently loaded?"
"Look up the Lookup function, filtering by fileID 0"

The agent will call list_tags_files to check what is already loaded, then use lookup_symbol or get_symbol_info to answer. For projects without a tags file on disk, it will call load_tags_file first.

Architecture

internal/
  parser/       Streaming line-by-line ctags parser
  indexer/      In-memory index (map[string][]Entry); lock-free single-pass
                parsing (ParseTagsFile) with deferred merge (InstallParsed)
  server/       MCP protocol layer (Server) + index lifecycle
                (IndexOrchestrator: discovery, background load, watch/rebuild)
  cmd/ctags-mcp/  Entry point (stdio transport)