Once called mcp-code-index, now (and hilariously unintentionally) referred to by its problematic abbreviation.
  • TypeScript 94.4%
  • JavaScript 5.4%
  • Go Template 0.2%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Claude d9e4b5b32d fix(daemon): install signal handlers before reporting ready
The daemon logged "Daemon listening" (its readiness signal, which the
shutdown test and any supervisor key off) before registering its
SIGTERM/SIGINT handlers. A signal delivered in that window took the
default disposition and killed the process without draining sessions or
checkpointing the WAL. Signalling immediately on readiness reproduced it
35/40 times for SIGINT and 15/20 for SIGTERM; it surfaced as the flaky
"exits 0 on SIGINT" test.

Move the readiness log after process.on() so readiness implies the
shutdown path is armed. Repro now 0/40 for both signals.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 09:36:24 -07:00
launchd first. 2026-07-13 14:18:54 -07:00
src fix(daemon): install signal handlers before reporting ready 2026-10-03 09:36:24 -07:00
.gitignore Rewrite README for the daemon; expand .gitignore 2026-07-13 14:29:17 -07:00
AGENTS.md first. 2026-07-13 14:18:54 -07:00
CLAUDE.md Trim CLAUDE.md to the essentials 2026-07-13 16:24:22 -07:00
eslint.config.js first. 2026-07-13 14:18:54 -07:00
package-lock.json fix: run the daemon on Node 26 and keep the plist off versioned Cellar paths 2026-10-03 08:48:59 -07:00
package.json fix: run the daemon on Node 26 and keep the plist off versioned Cellar paths 2026-10-03 08:48:59 -07:00
README.md Rewrite README for the daemon; expand .gitignore 2026-07-13 14:29:17 -07:00
tsconfig.eslint.json first. 2026-07-13 14:18:54 -07:00
tsconfig.json first. 2026-07-13 14:18:54 -07:00
vitest.config.ts first. 2026-07-13 14:18:54 -07:00

mcp-code-index

An always-on macOS daemon that gives Claude persistent, queryable code context across sessions and across projects. It indexes your code with tree-sitter (TypeScript, JavaScript, Svelte), stores symbols, call/import graphs, references, and session notes in SQLite, and serves symbol, graph, and semantic (vector) search over the Model Context Protocol.

One daemon serves every registered project. There is no per-project process and no MCP_CODE_INDEX_PROJECT env var: projects are registered explicitly (via the mci CLI or REST) or auto-registered the first time a tool call names a valid project root.

How it works

  • MCP over streamable HTTP at http://127.0.0.1:7420/mcp (stateful sessions).
  • Localhost REST registry at http://127.0.0.1:7420/api/* for managing tracked projects.
  • Bound to 127.0.0.1 only. Loopback is the trust boundary; both surfaces validate the Host/Origin headers to defend against DNS-rebinding. No auth/TLS (single-user model).
  • Refresh-on-query freshness: symbol/graph/search calls block on an incremental re-index first (inline for small deltas, a worker thread for large ones so the event loop stays free). Embeddings regenerate on idle; index_status reports embeddings_pending.
  • Per-project index DBs live in $XDG_CACHE_HOME/mcp-code-index/indexes/<hash>/index.db (fallback ~/.cache/...); a shared registry.db in the same cache root holds the project list and cross-project context links.

Requirements

  • Node.js >= 20 (macOS; launchd packaging is macOS-specific)

Install

npm install
npm run build
npm link          # puts `mci` and `mcp-code-index` on PATH
mci install-service   # load the launchd agent (runs at login, restarts on crash)

Run in the foreground instead of as a service with npm start (or node dist/index.js). Set MCP_CODE_INDEX_PORT to change the port (default 7420).

Claude Code integration

Register the daemon once, globally, in ~/.claude.json — it then serves every project:

{
  "mcpServers": {
    "code-index": { "type": "http", "url": "http://127.0.0.1:7420/mcp" }
  }
}

Managing projects (mci)

mci add [path]        # register a project (defaults to cwd); auto-register also works
mci list              # list registered projects
mci status            # daemon status: projects, sessions, embeddings pending
mci remove <id|path>  # stop tracking a project
mci install-service   # install + load the launchd agent
mci uninstall-service # unload + remove it

The same operations are available over REST: POST/GET/DELETE /api/projects, GET /api/status.

MCP tools

Every tool takes a required project argument (the absolute project root). A registered root resolves directly; an unregistered-but-valid root (contains .git or package.json) is auto-registered; anything else is rejected.

Tool Description
index_status Index health: file/symbol counts, stale files, embeddings_pending
index_stale List files needing re-indexing (modified, deleted, or new)
index_refresh Force re-indexing (bypasses the staleness check)
index_search Search symbols by name pattern or semantic similarity
index_symbol Look up a symbol's definition, callers, callees, and references
index_graph Traverse call/import relationships with depth control
index_annotate Store session context (decisions, patterns, notes)
index_context Retrieve stored context from previous sessions
index_embeddings Generate vector embeddings for semantic search
index_link Attach non-code context (issue/PR/repo/URL/note) to a symbol, file, or project
index_links Query context links for a symbol, file, or project (cross-project)
  1. index_refresh (or just query — freshness is automatic) to build the index.
  2. index_embeddings to generate vectors (also happens automatically on idle).
  3. index_search with semantic: true for natural-language queries.

Embeddings use all-MiniLM-L6-v2 (384 dims) via @huggingface/transformers, stored with sqlite-vec.

Development

npm run dev        # tsx watch
npm run test       # vitest (watch)
npm run test:run   # vitest once
npm run typecheck  # tsc --noEmit
npm run lint       # eslint

License

MIT