java-index MCPサーバー
コミット 2026年11月12日LSP経由でエディタへ、MCP経由でAIエージェントへ、軽量なJavaコードのナビゲーションとインデックス作成機能を提供します。
別名 slgobinath/java-index
- スター
- 8
- ツール
- —
- ダウンロード数(30日間)
- —
- 最新バージョン
- —
- 最終コミット
- 2026年11月12日
- ライセンス
- Apache-2.0
java-index MCPとは
java-indexはMCPサーバーです。 LSP経由でエディタへ、MCP経由でAIエージェントへ、軽量なJavaコードのナビゲーションとインデックス作成機能を提供します。.
プロジェクトのREADMEから生成された概要
java-index MCP server について
AIエージェントが作成したJavaコードをレビューする開発者は、java-indexを使用することで、フル機能のLanguage Serverのような重いリソースを消費することなく、複雑なコードベースをナビゲートできます。定義の検索、実装の確認、コール階層の表示といった、コードを読む際に不可欠なタスクを軽量に行う手段を提供します。プロジェクト全体をターゲットフォルダにコンパイルする従来のツールとは異なり、このMCP serverはturbineを使用してソースを解析し、宣言のサマリーを作成するため、プロジェクトファイルに書き込みを行うことはありません。
ユーザーは、Language Server Protocolを介してエディタから、またはModel Context Protocolを介してClaude CodeなどのAIエージェントからインデックスにアクセスできます。単一のデーモンがリポジトリのすべてのワークツリーにサービスを提供するため、複数のエディタウィンドウやエージェントセッションで同じインデックスを共有できます。この仕組みは、propertiesやYAMLファイルの設定キーを、それを読み取るJavaコードに直接紐付けることができるため、Spring BootやQuarkusの開発者にとって特に有用です。
できること
- 1Springの設定ナビゲーションpropertiesファイルの設定キーから、それを読み取るJavaコードへジャンプします。
- 2エージェント生成コードのレビューソースコードや依存関係のjarからクラスの実際のAPIを理解する機能を、AIエージェントに提供します。
プロジェクトの README から作成されています。すべてのユースケースは、その引用と照らし合わせて確認されています。
設定
JAVA_HOME
| 名前 | 説明 | 種類 |
|---|---|---|
| JAVA_HOME | — |
自動チェック
実行日 2026年11月14日完全なレポート →まだ検証されていません。サーバーには80点のスコア、重大な問題がないこと、および検証に必要な十分なデータが必要です。 チェックの仕組み · 点数を上げるための改善点
- ソースリポジトリがリンクされており、アクセス可能slgobinath/java-index is public and reachablegithub.com/slgobinath/java-index ↗合格
- パッケージがこのリポジトリをソースとして指定未チェック
- ビルドの来歴 (Sigstore 証明)未チェック
- オープンソースライセンス (OSI)Apache-2.0spdx.org/licenses/Apache-2.0.html ↗合格
- 過去1年以内にコミットまたはリリースありLast commit or release 2026-11-12github.com/slgobinath/java-index ↗合格
- OpenSSF Scorecard未チェック
- パッケージ名が有名なパッケージに酷似していない未チェック
- パッケージおよびその依存関係に重大な勧告なし未チェック
- モデルへの隠れた指示がない0 tool descriptions and the README read; no override phrases, credential paths or hidden characters合格
- 破壊的なツールがラベル付けされている未チェック
- リモートエンドポイントが MCP ハンドシェイクに応答未チェック
README
GitHubで見るjava-index
Fast, lightweight Java code navigation, mainly for reviewing code that AI agents write.
It handles the everyday questions you have when reading Java: where is this defined, who calls it,
what implements it, what does this library method actually look like, which config key does this
@Value read. Your editor talks to it over
LSP, and agents like Claude Code talk to
it over MCP. Both share the same index.
https://github.com/user-attachments/assets/9984c749-106f-4f31-ac43-a0c9f10f1f90
Contents
- Purpose
- How it's different
- java-index vs jdtls
- Benchmark results — projects, spring-petclinic, jackson-databind, Hibernate ORM
- What you get — in your editor (LSP), for AI agents (MCP)
- Install
- Set up
- How it works
- Classpath
- Configuration
- Limitations
- Reporting problems
- License
Purpose
More and more of our Java code is written by agents, and we spend more time reviewing it than
writing it. Reviewing needs basic navigation: go to definition, find callers, check
implementations. Full language servers like jdtls can do that, but they're built for writing code.
They're heavy, they start over for every git worktree, and they compile the project into the same
target/ folder the agent's build uses.
java-index only does the reading part. One small daemon serves every worktree of a repository, agents get the same index as tools, and it never builds or writes anything in your project. It's not a replacement for jdtls or your IDE. If you write Java by hand, keep using those.
How it's different
- No compiling. Sources are parsed with turbine (Google's header compiler) into small declaration summaries. Method bodies are only looked at when you ask about something inside them.
- One daemon per repo. All your editor windows, agent sessions and worktrees share one process. Files that are the same across worktrees are stored once; each worktree only keeps what it changed.
- Same answers for you and your agents. The editor gets definitions, references, hover and
hierarchies. Agents get the same information as simple tools that return text with
file:linelocations.
java-index vs jdtls
jdtls is a full language server for writing Java. java-index only covers reading it.
| java-index | jdtls | jdtls light | |
|---|---|---|---|
| Definition, references, hover, implementations, type and call hierarchy, symbols | ✓ | ✓ | partial (definition, hover and symbols in project sources; no references, implementations or hierarchies) |
| Compiler errors and warnings | — | ✓ | syntax errors only |
| Completion | — | ✓ | partial (no classpath) |
| Refactoring, quick fixes (beyond imports), formatting | — | ✓ | formatting only |
| Debugging, running tests | — | ✓ (with extensions) | — |
| Lombok and other annotation processors | — | ✓ | — |
| Spring Boot / Quarkus config keys linked to Java | ✓ | — (separate Spring Tools) | — |
| Tools for agents (MCP) | ✓ | — | — |
| One shared process for every worktree of a repository | ✓ | — (one per window) | — (one per window) |
Writes into your project (target/) |
never | yes, builds alongside yours | never |
Measured numbers on three open-source projects are in Benchmark results.
jdtls does a lot more work than java-index, since it compiles the project. Where jdtls compiles, java-index has to approximate, which means:
- No Lombok or annotation processors. turbine doesn't run them, so anything generated by
@Getter,@Data,@Builderand friends doesn't exist for java-index, and calls to it won't resolve. - Generated sources need a build first. Code in
target/generated-sources(MapStruct, protobuf, OpenAPI) gets indexed once it's there, but on a fresh clone it isn't. - Resolution isn't perfect. Some generic inference falls back to bounds, and reflection or framework wiring is invisible. When it isn't sure, it returns nothing instead of guessing. See Limitations.
- No compile errors. It can't tell you the code doesn't compile. Your build or CI does that.
So: if you write Java by hand, use jdtls or an IDE. If you mostly review what agents write, especially across several worktrees, java-index gets you the same navigation for a lot less time and memory.
Benchmark results
The benchmark opens each project in a fresh editor-like LSP client, waits
until the server answers a probe query correctly, then runs a fixed set of queries and checks each
answer. Every server is started cold (no caches) and warm (its caches from the previous run) three
times; the numbers are medians. Machine: Apple M5 Pro, 64 GB. Servers: java-index with the native
relay, jdtls 1.63.0 with VS Code's default settings, and the same jdtls in VS Code's lightweight
mode (java.server.launchMode: LightWeight, "jdtls light"), which only parses sources and doesn't
import the build.
Projects
| Project | Version | Size | Build |
|---|---|---|---|
| spring-petclinic | commit 500158f |
50 Java files, 4k lines | Maven, one module |
| jackson-databind | 2.23.0 | 1,300 Java files, 267k lines | Maven, one module |
| Hibernate ORM | 7.4.15 | 22,800 Java files, 2.7M lines | Gradle 9.5, about 25 modules |
Dependencies are downloaded before any run, and Hibernate is built once (./gradlew testClasses)
so its generated parser sources exist. The queries are in benchmark/cases.py,
cases_jackson.py and
cases_hibernate.py: definitions into the project, libraries and
the JDK, hover, references, implementations, type and call hierarchy, signature help and symbols.
spring-petclinic
A small Spring Boot app.
| java-index | jdtls | jdtls light | |
|---|---|---|---|
| Ready to answer, cold / warm start | 1.5 s / 0.4 s | 2.9 s / 1.9 s | 1.6 s / 1.3 s |
| Memory, one window | 256 MB | 1.8 GB | 337 MB |
| CPU time to open one window, cold | 7 s | 57 s | 10 s |
| Disk | 64 KB | 164 MB | 2 MB |
| Query latency (queries it answers) | 0.2–7.6 ms | 0.7–395 ms | 1.1–8.0 ms |
| Find references on a common method | 2 ms | 395 ms | not supported |
| 8 worktrees open at once: all ready | 1.6 s | 8.3 s | 2.7 s |
| 8 worktrees open at once: memory | 418 MB | 12.8 GB | 3.1 GB |
| 8 worktrees open at once: CPU / disk | 11 s / < 1 MB | 434 s / 1.3 GB | 66 s / 18 MB |
| Correct answers (17 queries per window) | 17 / 17 | 17 / 17 | 5 / 17 |
| References and implementations, location by location | same as jdtls | (baseline) | not supported |
jackson-databind
A larger library, about 60 times petclinic.
| java-index | jdtls | jdtls light | |
|---|---|---|---|
| Ready to answer, cold / warm start | 1.7 s / 0.5 s | 3.1 s / 2.2 s | 2.3 s / 2.0 s |
| Memory, one window | 328 MB | 1.8 GB | 619 MB |
| CPU time to open one window, cold | 23 s | 91 s | 56 s |
| Disk | 4 MB | 77 MB | 41 MB |
| Query latency (queries it answers) | 0.6–200 ms | 3.8–505 ms | 8.9–383 ms |
| Find references on a common method (383 callers) | 12 ms | 161 ms | not supported |
| Correct answers (17 queries) | 17 / 17 | 17 / 17 | 4 / 17 |
| References and implementations, location by location | same as jdtls | (baseline) | not supported |
Hibernate ORM
A large multi-module Gradle build, about ten times jackson-databind.
| java-index | jdtls | jdtls light | |
|---|---|---|---|
| Ready to answer, cold / warm start | 3.7 s / 2.8 s | 14.6 s / 3.7 s | not ready after 18 min |
| Memory, one window | 960 MB | 2.0 GB | |
| CPU time to open one window, cold | 46 s | 534 s | |
| Disk | 46 MB | 492 MB | |
| Query latency | 1.0–95 ms | 4.4–1,474 ms | |
| Find references on a common method (196 callers) | 52 ms | 1,474 ms | |
| Correct answers (18 queries) | 18 / 18 | 18 / 18 warm; references empty on every cold run | |
| References and implementations, location by location | same as jdtls | (baseline) |
jdtls needed one setting to import Hibernate at all: its Gradle import fails on Gradle 9 while
annotation processing is on (the default), so the benchmark sets
java.import.gradle.annotationProcessing.enabled to false for Gradle projects.
What the numbers say
Lightweight mode makes jdtls almost as cheap to start as java-index on a small project, but without
the build it has no classpath, and it takes none: java.project.referencedLibraries is ignored in
that mode. It can't do references, implementations, type or call hierarchy, signature help or
workspace symbols, and anything that depends on a library type (definitions and hover into
libraries, and on jackson-databind even picking the right overload of a project method) comes back
empty or wrong. On Hibernate it never became ready.
On a warm start java-index reads its saved file summaries and parses only files that changed, so most of its warm start on Hibernate is walking and hashing 22,800 files. That cache is the disk column: 64 KB to 46 MB, depending on project size.
To be fair about these numbers: it's three projects and 17–18 queries per project that we picked ourselves. We also ran the same kind of queries on a private multi-module Maven project (19 queries, all correct), but can't publish it.
What you get
In your editor (LSP)
| Feature | What you get |
|---|---|
| Go to definition | into your sources, dependency sources jars, the JDK's src.zip, or generated stubs |
| Find references | exact: occurrences that resolve to the same declaration, not just the same name |
| Hover | signature and javadoc, overrides and implementations, usage counts, deprecation with replacement |
| Implementations, type and call hierarchy | subtypes, overrides, supertypes, incoming and outgoing calls |
| Signature help and inlay hints | overloads while typing; parameter names, var and lambda types, member-chain types |
| Code actions | add a missing import, organize imports |
| Diagnostics | uses of @Deprecated APIs, with the suggested replacement |
| Symbols | file outline; classes, methods and fields across sources and libraries |
Spring Boot and Quarkus configuration. Keys in application*.properties and .yml are linked
both ways with the Java that uses them (@Value, @ConfigurationProperties, @ConfigProperty,
@ConfigMapping, getProperty("…")). Jump from a key in Java to where it is set in every profile,
or from a config file to the code that reads it. Hover shows the value per profile and, for library
keys such as server.port, the documented type, default and description. Spring's relaxed binding
works too.
For AI agents (MCP)
| Tool | What it answers |
|---|---|
java_api |
The real API of a class or member as this project compiles against it, from source, dependency jars or the JDK |
java_callers |
Usages of a method, constructor or field, with receiver types resolved so same-named methods on other types are excluded |
java_implementations |
All subtypes of a type or all overrides of a method |
java_outline |
A file's types and members with resolved signatures and line numbers |
java_find_type |
Classes by name, camel-case humps or qualified suffix |
java_config |
A Spring Boot / Quarkus config key: its value per profile, the Java that uses it, and library docs |
Every tool takes an optional worktree path to query another worktree of the same repository.
Grep is still fine for plain text search. java-index helps most with library APIs (no more
guessing signatures), common method names like save, get or execute, and type hierarchies.
Treat its answers as strong hints, not proof, since it can't see reflection or framework wiring.
Install
macOS (Apple Silicon) and Linux, with Homebrew. This repository is its own tap:
brew tap slgobinath/java-index https://github.com/slgobinath/java-index
brew trust --formula slgobinath/java-index/java-index
brew install java-index
jindex --version
Homebrew refuses formulae from third-party taps until you trust them, hence brew trust. Later
releases install with brew update && brew upgrade java-index.
This installs the jindex command: a small native relay, plus the daemon it starts, which runs on
Homebrew's OpenJDK (set JINDEX_JAVA to use another JDK 21+).
Anywhere else (Intel Macs, Windows, or without Homebrew): download java-index-<version>.jar
from the releases and use
java -jar /path/to/java-index-<version>.jar (Java 21+) wherever this README says jindex.
From source (JDK 21+):
mvn package # target/java-index.jar, runs the tests
JAVA_HOME=/path/to/graalvm-25 mvn -Pnative package # also target/jindex-relay, the native relay
target/jindex-relay works like jindex: it finds java-index.jar next to itself (or in
../target, ../lib) and starts the daemon with $JINDEX_JAVA, $JAVA_HOME/bin/java or java
from the PATH. Each editor window or agent session runs one relay; the native one uses about
12 MB instead of a JVM's 60–80 MB.
Set up
jindex starts (or connects to) the repository's daemon:
jindex # LSP over stdio, for editors (same as: jindex lsp)
jindex mcp # MCP over stdio, for agents (--root DIR to override the working directory)
jindex --help # usage; --version prints the version
If you run plain jindex in a terminal it will look stuck. It's just waiting for an editor to
talk to it. Press Ctrl-D to exit.
Claude Code (run in your repository):
claude mcp add java-index -- jindex mcp
Neovim (0.11+):
vim.lsp.config('java-index', {
cmd = { 'jindex' },
filetypes = { 'java' },
root_markers = { '.git', 'pom.xml', 'build.gradle', 'build.gradle.kts' },
})
vim.lsp.enable('java-index')
Helix (languages.toml):
[language-server.java-index]
command = "jindex"
[[language]]
name = "java"
language-servers = ["java-index"]
Emacs (eglot): (add-to-list 'eglot-server-programs '(java-mode "jindex"))
Zed: Zed's Java extension hard-wires jdtls, so editors/zed holds a small
extension that adds java-index as a Java server. Keep the Java extension for syntax highlighting,
install editors/zed with zed: install dev extension, then in settings.json:
"languages": {
"Java": { "language_servers": ["java-index", "!jdtls", "..."] }
}
The extension runs jindex from the PATH; to use the jar instead, add
"lsp": { "java-index": { "binary": { "path": "java", "arguments": ["-jar", "/path/to/java-index.jar"] } } }.
VS Code: Install it from the Visual Studio Marketplace. Java 21 is required to run the daemon. Disable the Red Hat Java extension in workspaces you review with java-index, or you get every answer twice. Settings are in its README.
Sublime LSP / Kate: register a custom stdio server running jindex for Java files.
How it works
editor ──stdio──► jindex (relay) ──unix socket──► daemon (one per repository)
editor ──stdio──► jindex (relay) ─────────────┤
agent ──stdio──► jindex mcp (relay) ─────────────┘
- Routing. Each client runs a small relay. It finds the repository's git common dir (shared by all its worktrees) and connects to that repository's daemon, starting it if needed.
- Shared index. Each source file becomes a declaration summary keyed by content hash. The first worktree indexed becomes the shared base snapshot; other worktrees store only the files that differ, plus unsaved editor buffers.
- Restarts. Summaries are saved per worktree under
JINDEX_HOME, keyed by content hash, so a restarted daemon only parses files that changed since. The cache is dropped when java-index is upgraded. - Dependencies. Jars are read lazily with turbine's bytecode binder, shared across worktrees
with the same build files. Definitions in libraries open their
-sources.jar, the JDK'ssrc.zip, or a stub generated from the class file. - Method bodies. Turbine skips method bodies, so the cursor is resolved from tokens: locals,
var, lambdas, anonymous classes and member chains with generic substitution, overload selection and lambda-based inference. - References. Each file summary carries a Bloom filter of its identifiers, so a search reads only files that may mention the name, then keeps the occurrences that resolve to the same declaration.
- Resources. The relay only forwards bytes; the daemon does the work. It uses a shrinkable heap, compacts when quiet and exits after 15 idle minutes.
Classpath
Resolved per worktree, in this order:
initializationOptions.classpath: a list of jars or jar directories..java-index/classpathin the worktree: one entry per line.- Maven (
pom.xml, uses./mvnwif present):dependency:build-classpath. - Gradle (
build.gradle[.kts], uses./gradlew): an init script that prints the compile classpaths.
Results are cached, keyed by the content of the build files, and recomputed when a build file changes.
Configuration
| Variable | Default | Purpose |
|---|---|---|
JINDEX_HOME |
$XDG_CACHE_HOME/java-index or ~/.cache/java-index |
sockets, daemon logs (<key>.log), extracted sources, cached classpaths and file summaries |
JINDEX_DAEMON_OPTS |
-Xmx1g |
extra JVM flags for the daemon |
JINDEX_IDLE_MINUTES |
15 |
daemon exits after this long with no connections |
JINDEX_JAVA |
java |
JDK (21+) the relay starts the daemon with |
LSP initialization options:
| Option | Default | |
|---|---|---|
classpath |
build tool | see Classpath |
inlayHints.parameterNames |
"literals" |
"all" or "none" |
inlayHints.types |
true |
var, lambda parameter and chain type hints |
deprecations |
true |
mark uses of deprecated APIs |
java -jar java-index.jar lsp --no-daemon and … mcp --no-daemon serve in-process without a daemon (the jar only; the native relay always relays), which is
handy for debugging.
Limitations
Resolution is approximate on purpose. Generic methods are inferred from their arguments and
lambda parameters from the expected functional interface, so stream().map(...).filter(...) and
forEach(item -> item.save()) resolve. Inference that needs the assignment target
(collect(Collectors.toList())) falls back to the type variable's bound. When java-index isn't sure,
it returns nothing instead of guessing. References are exact matches on the resolved declaration:
a call through a subclass that overrides a method counts for the override (the MCP java_callers
tool reports both, in separate groups).
Reporting problems
Missing and wrong answers are the most useful reports: most of the resolver's fixes so far came from real code it got wrong. Please open an issue with:
- the query (go to definition, references, …) and the line of code it was made on, or a small snippet that reproduces it;
- what you expected and what you got (nothing, or the wrong place);
jindex --version, the build tool, and whether the project uses Lombok or other annotation processors (those aren't supported);- the daemon log,
~/.cache/java-index/<key>.log(underJINDEX_HOMEif set). The log of the repository you were in is the one that mentions its path:grep -l "indexed /path/to/repo" ~/.cache/java-index/*.log.
License
Licensed under the Apache License, Version 2.0. The built jar bundles third-party libraries under Apache 2.0, EPL 2.0 and MIT; see NOTICE.
java-index MCPのよくある質問
java-indexはjdtlsやIDEの代わりになりますか?
いいえ、jdtlsやIDEの代わりになるものではありません。Java開発におけるコードの読み取りやレビュー、特にAIエージェントによる利用に特化して設計されています。
Lombokやその他のアノテーションプロセッサをサポートしていますか?
いいえ、turbineを使用しているため、Lombokやアノテーションプロセッサはサポートしていません。
Spring BootやQuarkusの設定確認に役立ちますか?
はい。propertiesやYAMLファイル内の設定キーと、それらを使用するJavaコードを紐付けます。