java-index MCP server
2026년 11월 12일 커밋LSP를 통한 에디터 지원 및 MCP를 통한 AI agent 지원으로 가벼운 Java 코드 탐색과 인덱싱 기능을 제공합니다.
다른 이름 slgobinath/java-index
- 스타
- 8
- Tools
- —
- 다운로드, 30일
- —
- 최신 버전
- —
- 마지막 커밋
- 2026년 11월 12일
- 라이선스
- Apache-2.0
java-index MCP란?
java-index은(는) MCP server입니다. LSP를 통한 에디터 지원 및 MCP를 통한 AI agent 지원으로 가벼운 Java 코드 탐색과 인덱싱 기능을 제공합니다.
프로젝트 README에서 생성된 요약
java-index MCP server 정보
AI agent가 작성한 Java 코드를 리뷰하는 개발자는 java-index를 사용하여 전체 언어 서버의 무거운 리소스 요구 사항 없이도 복잡한 코드베이스를 탐색할 수 있습니다. 이 도구는 정의 찾기, 구현 확인, 호출 계층 구조 보기와 같은 필수적인 읽기 작업을 수행할 수 있는 가벼운 방법을 제공합니다. 프로젝트 전체를 타겟 폴더로 컴파일하는 기존 도구와 달리, 이 server는 turbine을 사용하여 소스 코드를 파싱하고 선언 요약(declaration summaries)을 생성하므로 프로젝트 파일에 절대 쓰기 작업을 수행하지 않습니다.
사용자는 Language Server Protocol을 통해 에디터에서 인덱스에 접근하거나, Model Context Protocol을 사용하여 Claude Code와 같은 AI agent를 통해 접근할 수 있습니다. 단일 데몬이 저장소의 모든 worktree에 서비스를 제공하므로, 여러 에디터 창과 agent 세션이 동일한 인덱스를 공유할 수 있습니다. 이러한 구성은 Spring Boot 및 Quarkus 개발자에게 특히 유용하며, properties 또는 YAML 파일의 설정 키를 이를 읽어들이는 Java 코드와 직접 연결해 줍니다.
활용 사례
- 1Spring 설정 탐색properties 파일의 설정 키에서 해당 키를 읽는 Java 코드로 바로 이동합니다.
- 2agent 생성 코드 리뷰AI agent가 소스 코드나 의존성 jar로부터 클래스의 실제 API를 이해할 수 있는 능력을 제공합니다.
프로젝트의 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 agent를 위한 코드 읽기 및 리뷰 작업에 특화되어 설계되었습니다.
Lombok이나 다른 annotation processor를 지원하나요?
아니요, turbine이 이를 실행하지 않으므로 Lombok이나 annotation processor를 지원하지 않습니다.
Spring Boot나 Quarkus 설정 확인에 도움이 되나요?
네, properties 및 YAML 파일의 설정 키를 해당 설정을 사용하는 Java 코드와 연결해 줍니다.