Java semantic intelligence for Model Context Protocol (MCP) clients, powered by Eclipse JDT Language Server and ECJ.
java-lsp-mcp gives coding agents semantic navigation, diagnostics, compilation, tests, and refactoring previews. It never edits source files: changes are returned as hash-bound edits and optional diffs for the client to apply.
- Workspace and dependency navigation
- Maven, Gradle, Eclipse, modular, and unmanaged projects
- Current-snapshot diagnostics and ECJ compilation
- Parallel JUnit tests, suspended JDWP test launches, affected-test discovery, and JaCoCo coverage
- Read-only fixes, refactorings, import, and formatting previews
- Bounded, paginated agent-friendly results
- Node.js 24+ — the server runs on your Node; release launchers use
nodefromPATH. - A JDK — pointed to by
JAVA_HOMEor--tooling-jdk. It runs JDT LS and your tests. - A Java workspace — Maven, Gradle, Eclipse, modular, or unmanaged.
Release archives bundle JDT LS, JUnit, and JaCoCo. Everything else comes from the prerequisites above.
Download the archive for your platform from the releases page, unpack it, and run:
java-lsp-mcp/bin/java-lsp-mcp serve --workspace /absolute/path/to/project --trust-workspaceOn Windows use bin\java-lsp-mcp.cmd. Then verify with java-lsp-mcp doctor --workspace /project.
To use it in VS Code, add the launcher to .vscode/mcp.json in your project:
{
"servers": {
"java-lsp-mcp": {
"command": "/absolute/path/to/java-lsp-mcp/bin/java-lsp-mcp",
"args": ["serve", "--workspace", "/absolute/path/to/project", "--trust-workspace"]
}
}
}java-lsp-mcp print-config generic --workspace /project --trust-workspace prints the equivalent snippet for other clients.
Flags for serve (a java-lsp-mcp.json or java-lsp-mcp.toml file in the workspace and JAVA_LSP_MCP_* environment variables work too; flags win, then env, then the file):
| Flag | What it does |
|---|---|
--workspace |
The project to work on (absolute path, required). |
--trust-workspace |
Allow Maven/Gradle import, compilation, and test execution. Without it the server only reads code — review the project first. |
--tooling-jdk |
JDK that runs JDT LS (defaults to JAVA_HOME). |
--project-jdk |
JDK that runs your tests (defaults to the tooling JDK). |
--offline |
Never touch the network; Maven/Gradle resolve from caches only. |
--exclude-project |
Leave a module out of import, compilation, and tests (repeatable). |
--test-classpath-entry |
Extra directory or jar on the test runtime classpath (repeatable). |
--source-encoding |
Override Eclipse resource settings and automatic UTF-8/Windows-1252 source decoding. |
--timeout, --result-budget |
Shared operation timeout in ms and per-result output budget in bytes. |
--log-level, --max-heap, --result-mode |
Operational tuning; all logs go to stderr, never stdout. |
Source reads honor --source-encoding first, then Eclipse resource encoding settings. Without either, valid UTF-8 is preserved and other source files are read as Windows-1252, with a SOURCE_ENCODING_FALLBACK warning on stderr. Source files are never converted or modified. Set the encoding explicitly for other legacy encodings; invalid or unsupported explicit encodings still report errors.
Other commands: doctor checks the setup, version prints versions, describe-tools prints the complete machine-readable tool schemas, print-config generates client configuration, and clear-cache wipes the workspace index.
If Eclipse cannot restore cached workspace metadata because a generated resource disappeared, the server detects the failed recovery, rebuilds its JDT cache once, and retries startup.
Source-based tools support the encoding fallback above. Symbol targets accept either qualifiedName or path plus a required one-based line. Omit column to select the innermost declaration enclosing that line; supply a one-based Unicode code-point column for an exact position. Columns beyond the line are clamped to its end.
| Tool | Purpose |
|---|---|
java_status |
Readiness and runtime status |
java_outline |
Source declarations |
java_search_symbols |
Batch workspace/dependency symbols with cached pagination; omit line for every match, pass line to keep only the nearest declaration |
java_find_definition |
Batch symbol declarations; target by qualified name or file path + source line |
java_find_references |
Semantic usages with cached pagination; target by qualified name or file path + source line |
java_call_hierarchy |
Callers and callees; incoming callers default to production scope |
java_type_hierarchy |
Supertypes, subtypes, implementations |
java_diagnostics |
Current-snapshot diagnostics; fails fast with JDT_BUSY when JDT is busy and diagnostics are stale |
java_compile |
ECJ compilation with diagnostics, prerequisite recovery, and one clean retry when no syntax errors are confirmed |
java_update_projects |
Maven/Gradle configuration re-sync into JDT (force for full reimport) |
java_run_tests |
JUnit execution after automatic incremental compilation/recovery; persistent compile failures include diagnostics; debug: true launches suspended JDWP |
java_find_affected_tests |
Statically connected tests |
java_find_unused_code |
Candidate unused private members |
java_code_actions |
Available fixes and refactorings |
java_edit_preview |
Rename, action, import, format previews |
java_debug_targets |
Discover local JVMs started with the JDWP agent |
java_debug_attach, java_debug_sessions, java_debug_detach |
JDWP debug-session lifecycle |
java_debug_set_breakpoints, java_debug_wait_for_stop |
Replace source breakpoints (slash/backslash paths identify the same file) and bounded stop-event waits |
java_debug_threads, java_debug_stack_trace, java_debug_variables |
Filtered runtime thread/stack, local, collection, and object inspection |
java_debug_execute |
Continue and step over, into, or out |
java_debug_hot_swap |
ECJ/JDI Hot Code Replace with change and active-frame reporting |
java_search_symbols and java_find_definition require a queries array of 1–20 parameter objects. Batch related lookups to gather context in one call. Each entry has its own options, defaults, limit, and cursor. The response contains results in input order; failed entries contain error with code, message, and optional details, while other entries still complete. Top-level single-query parameters are rejected.
{"queries":[{"query":"OrderService"},{"query":"Customer","scope":"all"}]}{"queries":[{"target":{"qualifiedName":"com.example.OrderService"},"expand":["body"]},{"target":{"path":"src/Customer.java","line":12}}]}Symbol search and reference lookups retain independent result snapshots when another page is available. Subsequent pages reuse the collected matches and reference usage classification; a fresh request without a cursor performs a new lookup. The shared cache expires entries two minutes after creation and uses LRU eviction with limits of 32 result sets and 16 MiB of serialized results. Access does not extend expiry. Workspace changes and JDT reconnects invalidate cached continuations. An expired or evicted snapshot returns STALE_RESULT_SET; restart without a cursor. Results exceeding the cache byte limit bypass caching and retain ordinary pagination.
Source verification reads, decodes, and hashes each Java file once per verification, reusing the synchronized snapshot. Unchanged verification still offers the document to JDT (including retrying a failed initial synchronization) and avoids unnecessary watched-file change notifications.
Compilation retries an unsuccessful incremental build once as a clean build when current diagnostics contain no confirmed JDT syntax-error codes. Syntax errors are checked before severity filtering or pagination. Both attempts share the compilation timeout and preserve project exclusions and compileProjectOnly scope, including loaded prerequisites. Explicit clean builds, cancellation, unknown statuses, and request/configuration failures do not trigger a retry. java_compile.buildAttempts records the attempted build kinds and statuses; its diagnostics describe the final result.
java_run_tests compiles incrementally by default and uses the same recovery policy before launching tests. Successful builds are reused until the workspace generation changes; compile: "none" still skips compilation and compile: "clean" requests a clean build directly. Normal test results include buildAttempts (empty when a build was reused). Persistent build failures return status: "compile-failed" and a compilation object with build status, attempts, diagnostic counts, locations/messages, total, and diagnosticsTruncated for the bounded diagnostic sample. Test counts and test failures remain separate from compiler diagnostics. Debug launches use the same build policy; failed builds return compiler details in the tool error.
Run java-lsp-mcp describe-tools for the complete machine-readable tool schemas.
Contributions are welcome. See CONTRIBUTING.md.
Build from source with:
npm ci
npm run fetch-runtime
npm run check
bin/java-lsp-mcp serve --workspace /absolute/path/to/project --trust-workspaceReview SECURITY.md before granting workspace trust. Report vulnerabilities privately.
MIT. See LICENSE.