{
  "markdown": "# jarvis\n\n<!-- mcp-name: io.github.jarvis-intelligence/jarvis -->\n\n**Local-first code intelligence for coding agents.** Precomputed SCIP navigation\n(go-to-definition, find-references, call hierarchy, document symbols), Zoekt\nlexical search, cross-repo blast radius, and semantic search — exposed as MCP\ntools to Claude Code, Cursor, or any MCP client.\n\nRuns as a single stdio process reading local SQLite files. **No server, no auth,\nno network, nothing leaves your machine.**\n\n## Quick Start\n\n```bash\n# 1. External indexer binaries (scip, zoekt, per-language indexers)\ncurl -fsSL https://raw.githubusercontent.com/jarvis-intelligence/jarvis-index/main/setup.sh | sh\n\n# 2. jarvis itself\nuv tool install jarvis-mcp\n\n# 3. Index a repo (slug defaults to the directory name)\njarvis index /path/to/your/repo\n```\n\n**4. Register the MCP server.** Using Claude Code, install the plugin and it\nregisters itself:\n\n```\n/plugin marketplace add jarvis-intelligence/jarvis-index\n/plugin install jarvis@jarvis\n```\n\nAny other MCP client (or Claude Code without the plugin) registers manually:\n\n```bash\nclaude mcp add jarvis --scope user -- jarvis-server\n```\n\nThat's it — ask your agent \"find all references to `AuthService`\" and it will\ncall `findReferences` instead of grepping.\n\n<details>\n<summary>Other MCP clients (Cursor, Claude Desktop, any stdio client)</summary>\n\n```json\n{\n  \"mcpServers\": {\n    \"jarvis\": {\n      \"command\": \"jarvis-server\"\n    }\n  }\n}\n```\n\nIf your client can't find `jarvis-server` on `PATH` (GUI apps often don't\ninherit your shell's), use the absolute path from `which jarvis-server`.\n</details>\n\n<details>\n<summary>Running from a clone instead</summary>\n\n```bash\ngit clone https://github.com/jarvis-intelligence/jarvis && cd jarvis\nuv sync\nclaude mcp add jarvis --scope user -- uv --directory \"$(pwd)\" run jarvis-server\n```\n</details>\n\n## MCP tools\n\n| Tool | What it does |\n|------|--------------|\n| `goToDefinition` | Resolve a symbol to its defining file and range |\n| `findReferences` | Every occurrence of a symbol across the indexed repo |\n| `callHierarchy` | Incoming/outgoing calls for a symbol |\n| `documentSymbols` | Outline of every symbol defined in one file |\n| `searchCode` | Zoekt lexical/regex search, optionally filtered to one repo |\n| `semanticSearch` | Natural-language search — vector hits fused with Zoekt lexical hits and SCIP symbol-definition matches via reciprocal rank fusion |\n| `blastRadius` | Which *other* indexed repos depend on a package, up to 2 hops |\n| `getIndexStatus` | Published commit, freshness, staleness vs. a working tree |\n| `typeHierarchy` | Supertypes/subtypes — needs an index built with the bundled `scip`, see [limitations](#known-upstream-limitations) |\n\nEvery nav tool takes `repo` (the slug from `jarvis index`) plus a\ntool-specific `symbol` or `path`. All tools report failure the same way — a\n`{\"error\": \"...\"}` payload rather than a transport-level error, so a query bug\nnever kills the stdio server.\n\n## Requirements and limits\n\nRead this before installing — jarvis is deliberately narrow.\n\n- **macOS and Linux only.** Windows is not supported.\n- **One language per repo.** Language is detected by extension plurality across\n  git-tracked files; a polyglot monorepo gets indexed as whichever language has\n  the most files. Multi-language merge is out of scope. Override with\n  `--language`.\n- **SCIP navigation (`goToDefinition`, `findReferences`, etc.) covers four\n  language families:** TypeScript/TSX, Python, Java/Kotlin, Swift.\n  `jarvis index --search-only` additionally covers Go, Ruby, Rust, C,\n  C++, C#, PHP, Scala, shell, and SQL for `searchCode`/`semanticSearch`\n  only — no navigation.\n- **Navigation and search only — jarvis never edits code.** If you want an\n  agent that can perform semantic renames and refactors, you want\n  [Serena](https://github.com/oraios/serena); the two are complementary.\n- **Indexing is a separate, explicit step.** Nothing is live-analyzed. Run\n  `jarvis index` (or `jarvis watch`) to publish an index before querying.\n- **Requires external binaries** that `setup.sh` installs:\n\n  | Purpose | Binary | Source |\n  |---------|--------|--------|\n  | SCIP → SQLite conversion | `scip` | prebuilt, pinned `v0.9.0` (**minimum** — older versions silently drop occurrence ranges) |\n  | Lexical search | `zoekt-index` · `zoekt-webserver` | cross-compiled by [our CI](.github/workflows/build-zoekt.yml) — upstream publishes no binaries |\n  | TypeScript indexing | `scip-typescript` | `npm install -g` |\n  | Python indexing | `scip-python` | `npm install -g` |\n  | Swift indexing | `scip-swift` | prebuilt, **macOS arm64 only** |\n  | Java/Kotlin indexing | `scip-java` | detect-only — Docker image, asks before pulling |\n\n  Options: `--only <name>` to install one dependency, `--force` to reinstall,\n  `--help` for usage. Re-running is safe: anything already present is skipped.\n\nOptional extras:\n\n```bash\nuv tool install \"jarvis-mcp[watch]\"      # + watchdog, for `jarvis watch`\nuv tool install \"jarvis-mcp[semantic]\"   # + lancedb/sentence-transformers/tree-sitter, for semanticSearch\n```\n\n## Why it's built this way\n\n**Storage is the seam.** The runtime half only ever reads down into it; the\nindexing half only ever writes up into it; the two share no other contract:\n\n![jarvis layered architecture](docs/assets/jarvis-layers.png)\n\nThree things worth reading the diagram for:\n\n- **The runtime path never writes.** Queries open a published `index-<sha>.db`\n  read-only (`mode=ro&immutable=1`). Index files are never mutated in place.\n- **Publishing is atomic.** A reindex writes a new versioned `.db`, populates\n  the package graph, and runs `zoekt-index` — only once *all* of that succeeds\n  does `os.replace` (POSIX `rename(2)`) flip the small `current` pointer. A\n  query already reading the old file keeps working; there is no downtime\n  window, and a failure anywhere leaves the previously published index live.\n- **The package graph is rebuilt, not accumulated.** Each reindex clears that\n  repo's own outgoing edges before recomputing them, so a removed dependency's\n  edge is retracted — `blastRadius` always reflects each repo's *last* index\n  run.\n\nEditable source:\n[`docs/assets/jarvis-layers.dot`](docs/assets/jarvis-layers.dot) (Graphviz).\nLayer-by-layer detail, the full index pipeline, and the semantic path are in\n[`docs/system-architecture.md`](docs/system-architecture.md).\n\nCore query/search logic is ported from an internal reference implementation;\nthe enterprise shell (FastAPI, Postgres, hosted-git auth, Cloud Build) is\ndropped in favor of a single stdio process reading local SQLite files.\n\n## Indexing a repo\n\n```bash\njarvis index /path/to/your/repo            # slug defaults to the directory name\njarvis index /path/to/your/repo --slug foo # or pick one explicitly\njarvis index /path/to/your/repo --scheme MyScheme # Swift repo with an ambiguous Xcode scheme\njarvis index /path/to/your/repo --language python # force the language instead of detecting it from git-tracked files\njarvis index /path/to/your/repo --semantic-include vendor/generated # force-include a path the generated-file filter would otherwise skip\njarvis index /path/to/your/repo --search-only # skip SCIP indexing; publish only Zoekt + semantic search\njarvis index /path/to/your/repo --fallback-search-only # opt in: if the indexer fails mid-build, degrade to search-only (status `degraded`) instead of failing; the next reindex retries the full build\njarvis list\njarvis status foo\njarvis reindex foo\njarvis forget foo\n```\n\n`status` (as shown by both `list` and `status`) is usually `indexed` or\n`failed`, but can also be `partial`: the index published real symbols but no\nnavigable positions (an indexer/converter bug) — check the stderr warning\nfrom `jarvis index` for details.\n\n`--semantic-include` is repeatable — pass it once per path prefix to\nforce-include several. Like `--scheme` and `--language`, once set there is no flag to clear\nit; change it by re-running `jarvis index` with the new value(s).\n\n**Language detection** counts source files by extension **across git-tracked files** and picks the winner —\none language per index:\n\n| Extensions | Indexer |\n|------------|---------|\n| `.ts` `.tsx` | `scip-typescript` |\n| `.py` | `scip-python` |\n| `.java` `.kt` | `scip-java` |\n| `.swift` | `scip-swift` |\n\nTies break by fixed priority (`.ts` → `.tsx` → `.py` → `.java` → `.kt` → `.swift`).\n`.git`, `node_modules`, `.venv`, `__pycache__`, `dist`, and `build` are\nskipped. Reading git rather than walking the filesystem is deliberate: a walk\nalso counts gitignored scratch directories, which can outnumber a repo's own\ncode and pick a language it doesn't use.\n\nThe pipeline then runs: chosen indexer → `scip expt-convert` → populate the\npackage dependency graph (`packages`/`edges` tables in `registry.db`) →\n`zoekt-index` into `~/.jarvis/.zoekt` → copy to\n`~/.jarvis/scip/_/<slug>/_/index-<sha>.db` → atomic `current` pointer flip →\nregistry update.\n\n> The `scip/_/<slug>/_/` path shape reuses the vendored `IndexConnectionCache`'s\n> `(project, repo, branch)` 3-tuple layout with the outer two pinned to `_` (see\n> [`src/jarvis/config.py`](src/jarvis/config.py)). It is not a user-facing\n> contract — only `<slug>` matters when calling tools.\n\nSwift indexing works end-to-end. It requires `scip >= v0.9.0`: older converters\ncannot read scip.proto's `typed_range` oneof, which is the only range encoding\n`scip-swift` emits, and silently produce an index with no navigable positions.\n`jarvis index` refuses an older `scip` rather than publishing one.\n\nIndexing a Swift repo with code-signed app-extension targets additionally requires\n`scip-swift >= v0.1.2`: earlier versions pass no code-signing overrides to `xcodebuild`, which\nthen fails provisioning for every signed target before compiling anything. Because `setup.sh`\nskips any dependency that is merely *present*, an existing install is **not** upgraded by\nre-running it — use `sh ./setup.sh --only scip-swift --force`.\n\n## Watching a repo (auto-reindex)\n\n```bash\njarvis watch /path/to/your/repo             # debounce defaults to 5s\njarvis watch /path/to/your/repo --debounce 3\njarvis watch /path/to/your/repo --scheme MyScheme\njarvis watch /path/to/your/repo --language python\n```\n\nRuns in the foreground (not a daemon) using `watchdog` — install it with the\n`watch` extra. A burst of file changes (e.g. an editor's atomic save touching\nseveral files) coalesces into exactly **one** reindex. The reindex fires once\n`--debounce` seconds (default 5) have passed since the *last* file change —\nthis prevents thrashing on rapid edits. `.git`, `node_modules`, `.venv`,\n`__pycache__`, `dist`, and `build` are ignored.\n\n## Tool details\n\n- **`getIndexStatus`** takes an optional `repo_path` (the repo's local git\n  working directory) to compare the published commit against\n  `git rev-parse HEAD`. Omitted, freshness is reported without a staleness\n  check — never `stale: true` without evidence.\n- **`searchCode`** takes `query` plus an optional `repo` filter. On first call\n  it lazy-spawns an embedded `zoekt-webserver` (pidfile'd so a second jarvis\n  process reuses it instead of spawning a duplicate; killed on clean exit via\n  `atexit`).\n- **`blastRadius`** takes `repo` plus `symbol_or_package` (e.g. `\"npm:@scope/\n  name\"`, the same `\"{manager}:{name}\"` string `jarvis index` derives from\n  each repo's SCIP symbols). Returns every other indexed repo whose package\n  depends on it, up to 2 hops, each tagged with its hop distance. The package\n  graph has no per-node timestamp, so `freshness` is always `\"unknown\"` here —\n  an honest limitation of the schema, not a bug. Cross-repo edges resolve by\n  exact package name against whatever has *already* been indexed: index the\n  dependency first, or re-run `jarvis index`/`reindex` after indexing it,\n  for an edge to appear. Each reindex retracts that repo's own stale edges\n  before recomputing them, so a removed dependency's edge disappears too —\n  the graph always reflects each repo's *last* index run, not an\n  accumulation of every run it's ever had.\n- **`semanticSearch`** takes `repo` plus a natural-language `query`. Requires the\n  optional `semantic` extra. Results fuse a LanceDB vector search over\n  tree-sitter-chunked code with `searchCode`'s Zoekt hits via reciprocal rank\n  fusion. Raises a clear error if the repo has never been indexed with the extra\n  installed (`jarvis reindex <slug>` after installing it builds the missing\n  table); indexing itself is non-fatal — a failure there never blocks the rest\n  of `jarvis index`. Semantic indexing also respects `.gitignore` (on top of\n  the hardcoded ignore-directory list) and skips any file over 1 MB, in addition\n  to the existing generated-file banner/long-line detection —\n  `--semantic-include` overrides all three.\n\n### Known upstream limitations\n\nThese are real behaviors of the underlying SCIP tooling (`scip expt-convert`\nas of v0.9.0, `scip-java`, `scip-kotlinc`), not jarvis bugs:\n\n- **`typeHierarchy` returns an explicit `{\"error\": ...}`**, not empty arrays, on\n  indexes built with an unpatched upstream `scip` — that converter declares\n  `global_symbols.relationships` in its schema but never writes it. An empty\n  result would wrongly assert \"no supertypes\"; the error says \"cannot tell\"\n  instead. setup.sh installs a fork build carrying the fix, so a fresh\n  `jarvis reindex <slug>` makes the tool work. Reported upstream:\n  [scip-code/scip#464](https://github.com/scip-code/scip/issues/464), fix\n  [scip-code/scip#465](https://github.com/scip-code/scip/pull/465) (open, CI green).\n- **`displayName` / `kind` are backfilled from the symbol string.** The converter never populates\n  `global_symbols.display_name`/`.kind`, so `query.py`'s `_display_and_kind` parses both from the\n  SCIP symbol string whenever the database columns are empty (which they still normally are) —\n  `documentSymbols` returns real values in practice; only a genuinely unparseable symbol falls\n  through to `null`.\n- **`searchCode`'s `repo` filter matches Zoekt's own repository name**, which\n  `jarvis index` now names after the slug via `zoekt-index -meta` — so this\n  no longer diverges for repos indexed with current code. Shards published by\n  an older jarvis still carry their old directory-derived name until you\n  `jarvis reindex <slug>`.\n- **`scip-java` can't index Android/Gradle repos at all** — its Gradle plugin\n  keys off Gradle's standard source sets, which AGP replaces with its variant\n  model, so the build emits zero SCIP shards\n  ([scip-java#177](https://github.com/scip-code/scip-java/issues/177)).\n- **Kotlin indexing requires an exact Kotlin version match** — `scip-kotlinc`\n  is compiled against one pinned Kotlin release (`SCIP_JAVA_KOTLIN` in\n  `setup.sh`, currently `2.2.0`); its compiler-plugin API is internal and\n  unstable even across patch releases, so any other version fails.\n  Both cases are detected automatically from the indexer's own failure output\n  and degrade to `--search-only` rather than failing outright.\n- **Maven-built Java repos need bash >= 4.4 on macOS** — scip-java's generated\n  `javac` wrapper (`#!/usr/bin/env bash`, `set -eu`) expands\n  `\"${LAUNCHER_ARGS[@]}\"` unguarded, which errors on bash < 4.4; macOS ships\n  only 3.2, so the build dies at `default-compile` with\n  `LAUNCHER_ARGS[@]: unbound variable`. `setup.sh` works around it by linking\n  `~/.jarvis/shims/bash` to a newer bash and putting that one directory\n  first on `PATH` for the indexer. If no bash >= 4.4 is installed, indexing\n  fails with the remedy rather than degrading to `--search-only` — unlike the\n  two cases above, this one is fixable (`brew install bash`), and a persisted\n  `--search-only` cannot be un-set.\n\n## Configuration\n\n**Data directory** (default `~/.jarvis`):\n```bash\nJARVIS_DATA_DIR=/custom/path jarvis index /path/to/repo\n```\n\n**Environment variables:**\n- `JARVIS_DATA_DIR` — override default `~/.jarvis` for all indexes and registry\n- `JARVIS_FALLBACK_SEARCH_ONLY` — default the opt-in degrade-to-search-only fallback\n  on for repos indexed without an explicit `--fallback-search-only` /\n  `--no-fallback-search-only` flag. Accepts `1`/`true`/`yes`/`on`\n  (case-insensitive); any other value is treated as off with a one-line warning.\n- `JARVIS_EMBEDDING_QUERY_PREFIX` / `JARVIS_EMBEDDING_DOC_PREFIX` — override the\n  query/document instruction prefix applied before embedding. Auto-detected for bge-m3,\n  e5, and nomic-embed; set these if using a different model that needs one — `semanticSearch`\n  warns when an unlisted model has no prefix configured.\n\n## Agent skills\n\nThree agent skills ship in the Claude Code plugin, under `plugin/skills/`:\n\n- `jarvis-setup` — install, register, index, verify.\n- `jarvis-use` — prefer jarvis for structural queries (find references, go-to-definition, hierarchy).\n- `jarvis-issues` — file jarvis bugs/features via `gh`.\n\nInstall them, and register the MCP server, with:\n\n```\n/plugin marketplace add jarvis-intelligence/jarvis-index\n/plugin install jarvis@jarvis\n```\n\nSee [Quick Start](#quick-start) above for the manual registration alternative.\n\n## Standards\n\nBlob decoding follows the [SCIP protocol](https://scip-code.org/docs.html):\n`scip_pb2.py` is generated from `scip.proto` at `scip-code/scip` tag\n**v0.9.0** (regenerated up from v0.7.0, which lacked the `typed_range` oneof\n`scip-swift` requires), and occurrence/relationship blobs are decoded as real\n`scip.Document` / `scip.SymbolInformation` messages.\n\nThe SQLite layer (`documents`, `chunks`, `global_symbols`, `mentions`,\n`defn_enclosing_ranges`) is **not** part of that published spec — it is the\noutput shape of the experimental `scip expt-convert` sub-command, verified by\nhand against a real index. Treat it as a moving target across `scip` releases.\n\n## Tests\n\n```bash\nuv run pytest\n```\n\nIntegration tests that shell out to the real `scip-python` / `scip` /\n`zoekt-index` binaries are marked `integration`:\n\n```bash\nuv run pytest -m \"not integration\"   # unit only\nuv run pytest -m integration         # real-binary pipeline\n```\n\n## Documentation\n\n- [`docs/project-overview-pdr.md`](docs/project-overview-pdr.md) — scope, value prop, out-of-scope items\n- [`docs/system-architecture.md`](docs/system-architecture.md) — architectural guarantees, storage layout, query paths\n- [`docs/codebase-summary.md`](docs/codebase-summary.md) — module map, test coverage\n- [`docs/code-standards.md`](docs/code-standards.md) — code patterns and conventions\n- [`docs/project-roadmap.md`](docs/project-roadmap.md) — all phases complete, future ideas\n\nAll 4 planned phases are shipped — see\n[`plans/0724-2316-jarvis-mcp-implementation/plan.md`](plans/0724-2316-jarvis-mcp-implementation/plan.md).\n\n## License\n\n[MIT](LICENSE)\n",
  "bytes": 18736,
  "sha": "1127c38d681c14153bee4e464b4aca5d6bdb4c1107a4795459d82db7fc83a7bc",
  "repo_slug": "phuongddx/codeintel",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_phuongddx_codeintel_2fb3ee73/readme"
}