Java Code Indexer
JVM AST-level code indexer (Java/Kotlin/Scala) with SQLite storage, exposed as an MCP server for AI coding assistants.
中文 | English
Why
AI coding assistants exploring unfamiliar JVM projects today:
Features
Multi-Language Support
Annotation Recognition
Supports 30+ annotations across major frameworks:
Spring Ecosystem Support
Installation
Requires Java 21+ (JRE or JDK). Docker and Native Image options bundle the runtime.
Option 1: Download from GitHub Releases (recommended)
# Fat JAR (all platforms)
curl -LO https://github.com/Lincoln-cn/JCodeIndexer/releases/latest/download/java-code-indexer-VERSION.jar
# Native Image (Linux)
curl -LO https://github.com/Lincoln-cn/JCodeIndexer/releases/latest/download/java-code-indexer-VERSION-linux-amd64.tar.gz
# Native Image (macOS)
curl -LO https://github.com/Lincoln-cn/JCodeIndexer/releases/latest/download/java-code-indexer-VERSION-darwin-arm64.tar.gz
# Native Image (Windows)
curl -LO https://github.com/Lincoln-cn/JCodeIndexer/releases/latest/download/java-code-indexer-VERSION-windows-amd64.zip
Option 2: Build from source (requires Java 21+ and Maven 3.8+)
git clone https://github.com/Lincoln-cn/JCodeIndexer.git
cd java-code-indexer
mvn package -q -DskipTests
# output: target/java-code-indexer-*-shaded.jar
Option 3: Docker
docker pull sodlinken/jcodeindexer:latest
Quickstart
# 1. Index your JVM project (Java/Kotlin/Scala)
java -jar java-code-indexer-VERSION.jar --project-root /path/to/your/project --index
# 2. Start the MCP server (stdio, for Claude Code / Qwen Code / Cursor)
java -jar java-code-indexer-VERSION.jar --project-root /path/to/your/project
That's it. Your AI assistant can now query your codebase structure instead of reading files.
CLI Reference
java -jar java-code-indexer-VERSION.jar [options]
Options:
--project-root <path> JVM project root directory (default: current dir)
--data-dir <path> Index data directory (default: .jindexer)
--init Initialize database schema only
--index Run indexer (extract symbols/references/calls)
--status Show index statistics
--search <query> Search directly (no MCP server needed)
--export <file> Export index data to JSON file
--version Show version number
--help, -h Show help
No flags → start MCP server over stdio.
MCP Tools (26 tools)
When running as an MCP server, Java Code Indexer exposes these tools:
get_call_graph Parameters
{
"method_name": "com.example.Service.save",
"direction": "both", // "callers" | "callees" | "both"
"filter": "project", // "all" | "project" | "jdk"
"limit": 50 // max results per direction
}
get_call_graph Callee Type Inference
For method calls that cannot be resolved, the system automatically infers the type and returns a type field:
Configuration
Create .jindexer/config.yaml in your project root (optional):
# === Project Paths ===
# Project root (CLI --project-root overrides this)
project_root: /path/to/project
# Data directory (stores index.db)
data_dir: .jindexer
# === Indexing ===
# Number of indexing threads
indexing_threads: 4
# Max file size to index (KB)
max_file_size_kb: 512
# Extract Javadoc comments
extract_javadoc: false
# Follow symbolic links
follow_symlinks: false
# Include patterns (files to index)
include_patterns:
- "**/*.java"
- "**/*.yml"
- "**/*.yaml"
- "**/*.properties"
- "**/*.env"
- "**/pom.xml"
- "**/build.gradle"
- "**/build.gradle.kts"
# Exclude patterns (files to skip)
exclude_patterns:
- "**/target/**"
- "**/build/**"
- "**/.git/**"
- "**/node_modules/**"
# === Storage ===
# Database filename
db_name: index.db
# === Logging ===
# Log level: DEBUG, INFO, WARN, ERROR
log_level: INFO
# Enable verbose logging
verbose: false
# === File Watcher ===
# Enable automatic re-indexing on file changes
watch_enabled: true
# Watch mode: event (NIO WatchService) or polling (SHA-1 check)
watch_mode: event
# Watch interval in seconds (polling mode)
watch_interval_seconds: 5
# Debounce interval in ms (event mode)
watch_debounce_ms: 500
# Directories to exclude from watching
watch_exclude:
- "**/target/**"
- "**/build/**"
- "**/.git/**"
- "**/node_modules/**"
# === Multi-project Mode ===
# Define multiple projects to index together
projects:
- name: backend
root: /path/to/backend
- name: frontend
root: /path/to/frontend
# === Auto Discover ===
# Auto-discover Maven/Gradle sub-modules
auto_discover: true
Configuration Priority
CLI flags → environment variables → config file → defaults.
All Configuration Options
AI Assistant Integration
Claude Code / Cursor / Qwen Code
Add to your MCP configuration:
{
"mcpServers": {
"java-code-indexer": {
"command": "java",
"args": ["-jar", "/path/to/java-code-indexer-VERSION.jar", "--project-root", "/path/to/project"]
}
}
}
Docker Integration
{
"mcpServers": {
"java-code-indexer": {
"command": "docker",
"args": ["run", "--rm", "-i", "-v", "/path/to/project:/project", "sodlinken/jcodeindexer:latest", "--project-root", "/project"]
}
}
}
How It Works
┌──────────────┐ MCP (stdio) ┌──────────────────────────────────────┐
│ AI Assistant │ ◄─────────────────► │ Java Code Indexer │
│ (Qwen/Claude)│ JSON-RPC │ │
└──────────────┘ │ Phase A: Parallel Parse (no lock) │
│ ┌────────────────┐ │
│ │ Virtual Threads│──► Parsers: │
│ │ (concurrent) │ JavaParser │
│ │ │ KotlinParser │
│ │ │ ScalaParser │
│ └───────┬────────┘ │
│ ▼ │
│ Phase B: Batch Write (transactional) │
│ ┌────────────────┐ │
│ │ SQLite (WAL) │ │
│ │ symbols │ │
│ │ references │ │
│ │ call_graphs │ │
│ │ annotations │ │
│ │ configs │ │
│ │ dependencies │ │
│ └────────────────┘ │
└──────────────────────────────────────┘
Indexing Strategy (Two-Phase Architecture)
- Walk JVM source files (Java/Kotlin/Scala) via filesystem traversal
- Parallel SHA-1 comparison — pre-fetch all file metadata (single SQL), compute hashes concurrently via virtual threads
- Phase A — Parallel Parse (no DB lock): virtual threads parse files concurrently, extracting symbols, references, call relationships, annotations, and code chunks. Each file is parsed only once (AST reused for chunking).
- Phase B — Batch Write (single transaction per 50 files): all DB operations batched — references/calls use
executeBatch(), stale files deleted viaINclauses, stats merged into a single SQL query. - POM/Gradle parsers extract dependency information
- Config parsers extract YAML/Properties/.env entries
- All data upserted into embedded SQLite (WAL mode)
- FTS5 full-text search indexes auto-synced via triggers
Database Schema
Thirteen core tables:
symbols— classes, methods, fields with location and signaturesreferences— symbol usage locations across the codebasecalls— method call relationships (caller → callee)annotations— symbol annotations with attributeschunks— code slices at class/method granularityfile_meta— SHA-1 hashes for incremental indexingconfig_entries— YAML/Properties/.env key-value pairsdependencies— Maven/Gradle dependency declarationsapi_routes— Spring Boot API route mappings (URL → Controller)bean_dependencies— Spring Bean injection relationshipstest_mappings— Test class to source class associationsindex_metadata— Index metadata (key-value store)code_metrics— Code metrics (LOC, method/field counts, complexity)
Plus FTS5 full-text search tables (symbols_fts, chunks_fts) with auto-sync triggers.
Database Migration
The database uses automatic migration to handle schema upgrades:
- No data loss: Migrations use
ALTER TABLE ADD COLUMNwhich preserves existing data - Idempotent: Repeated migrations are safely ignored (duplicate column errors)
- Automatic: Database upgrades automatically on startup
When upgrading from an older version, the database schema is automatically updated without deleting data files. If the database is corrupted, it will be automatically rebuilt.
Architecture
src/main/java/com/sodlinken/jindexer/
├── cli/ # CLI entry point
├── mcp/ # MCP server (JSON-RPC over stdio)
├── config/ # YAML config loader
├── storage/ # SQLite schema & StorageService (including FTS5)
├── indexer/ # Two-phase incremental indexing engine (parallel parse + batch write)
├── parser/ # Java, Kotlin, Scala, POM, Gradle, Config parsers
├── chunker/ # Code chunking (class/method slices)
├── search/ # Structured search (FTS5 full-text)
├── model/ # Data models (Symbol, Call, Chunk, Annotation, etc.)
└── util/ # SHA-1 hashing utility
Distribution
GitHub Release
Release workflow is automated via GitHub Actions:
Development
# Clone and build
git clone https://github.com/Lincoln-cn/JCodeIndexer.git
cd JCodeIndexer
mvn package -q -DskipTests
# Run tests
mvn test
# Index this project itself
java -jar target/java-code-indexer-*-shaded.jar --project-root . --index
# Start MCP server
java -jar target/java-code-indexer-*-shaded.jar --project-root .
Release Workflow
GitHub Actions automated release process:
- Push
v*tag or manual trigger - Build Fat JAR
- Build Native Image (Linux amd64, macOS arm64, Windows amd64)
- Build Docker image
- Create GitHub Release and upload artifacts
# Create release tag (replace VERSION with actual version)
git tag vVERSION
git push origin vVERSION
Testing
# Run all tests (384+ tests)
mvn test
# Run specific test class
mvn test -Dtest=JavaParserAdapterTest
# Run performance benchmarks
mvn test -Dtest=PerformanceBenchmarkTest
What It Is NOT
- Not a code execution sandbox
- Not a test runner or linter
- Not a replacement for LSP/IDE features
- Not AI-generated summaries (symbol extraction is deterministic)