JDTLS-MCP
Provides Java language intelligence tools (hover, definition, references, completion, document symbols, workspace symbols, diagnostics) by embedding the Eclipse JDT Language Server directly in the same JVM.
jdtls-mcp
Expose the Eclipse JDT Language Server to LLM agents via the Model Context Protocol (MCP)
Overview
jdtls-mcp is an Eclipse application that extends
eclipse-jdtls with a new
OSGi bundle (org.eclipse.jdt.ls.mcp) that exposes Java language intelligence
as Model Context Protocol (MCP) tools.
┌─────────────────────────────────────────────────────────┐
│ Eclipse / Equinox OSGi container (one JVM process) │
│ │
│ ┌───────────────────────┐ ┌──────────────────────┐ │
│ │ org.eclipse.jdt.ls.core│ │ org.eclipse.jdt.ls │ │
│ │ (jdtls: handlers, │◄──│ .mcp │ │
│ │ project manager, JDT) │ │ (McpApplication + │ │
│ └───────────────────────┘ │ JdtlsMcpTools) │ │
│ └──────────┬───────────┘ │
└──────────────────────────────────────────│───────────────┘
│ MCP over stdio
┌─────────▼─────────┐
│ LLM / MCP client │
│ (Claude, GPT, …) │
└────────────────────┘The MCP bundle calls jdtls handler classes directly in the same JVM — no subprocess spawning, no network hop, no second Java process. LLM agents talk to the Eclipse application via stdio using the MCP protocol.
How it works
Target platform (
org.eclipse.jdt.ls.mcp.target) references the jdtls snapshot p2 repository, Eclipse 2025-12 release train, LSP4J 0.24.0, and the MCP Java SDK + langchain4j from Maven Central (wrapped as OSGi bundles by Tycho'smissingManifest="generate"feature).Plugin bundle (
org.eclipse.jdt.ls.mcp) is a standardeclipse-pluginmodule built by Tycho. It declaresRequire-Bundle: org.eclipse.jdt.ls.coreand calls jdtls handler classes (HoverHandler,NavigateToDefinitionHandler,ReferencesHandler,CompletionHandler,DocumentSymbolHandler,WorkspaceSymbolHandler) directly.MCP Application (
McpApplication) registers under the extension pointorg.eclipse.core.runtime.applicationsasorg.eclipse.jdt.ls.mcp.app. When selected as the Eclipse application, it initialises the jdtls workspace and starts an MCP server on stdio.Product (
org.eclipse.jdt.ls.mcp.product) packages all jdtls bundles together with the new MCP bundle into a distributable Eclipse product.
Related projects
sunix/java-lsp-mcp-server
A parallel experiment tackling the same goal — exposing jdtls to AI agents via MCP — but with a fundamentally different architecture.
Aspect | jdtls-mcp (this project) | java-lsp-mcp-server |
Architecture | Embedded — runs as an Eclipse OSGi product | External controller — runs jdtls as a managed subprocess |
MCP transport | stdio (NDJSON, one JSON object per line) | HTTP / SSE |
jdtls version | Fixed at build time via the Tycho target platform | Auto-downloaded at runtime (configurable URL or latest) |
Distribution | Pre-built platform archives (~63 MB each) | Standard Quarkus JAR or GraalVM native binary |
Workspace | Fixed at server startup (CLI argument) | Changed at runtime via |
Java required | Java 21 | Java 25 |
Framework | Tycho / OSGi | |
LSP tools | hover, document symbols, references, workspace symbols, definition, diagnostics | document symbols, completions, diagnostics, format, definition |
Lifecycle tools | None (transparent to the LLM) |
|
When to use which:
jdtls-mcp — zero operational overhead for the LLM agent; the server is ready when the process starts. Good fit for stdio-only MCP clients and fixed-workspace setups.
java-lsp-mcp-server — better for HTTP-based MCP clients, multi-workspace scenarios, or environments where you want the agent to control the jdtls lifecycle directly. The auto-download removes the need to ship a pre-built product per platform.
Improvements this project could borrow
Looking at what java-lsp-mcp-server has that this project currently lacks:
Missing tool | LSP request | What it enables |
|
| Let the agent request completions at a position — useful for code generation workflows |
|
| Normalise generated code before committing |
Both are available through the jdtls handlers already on the classpath
(CompletionHandler, FormattingHandler), so
they can be added to JdtlsMcpTools.java following the same pattern as the
existing tools.
Technology stack
Library | Role |
OSGi / Eclipse plugin build system | |
Java compiler, handlers, project manager (via p2) | |
MCP Java SDK ( | MCP server protocol + stdio transport |
| |
LSP types used by jdtls handlers |
Available MCP tools
Tool | Description |
| Get hover information (Javadoc, type info) at a position |
| Find the definition of a symbol |
| Find all references to a symbol |
| Get code completion suggestions |
| List all symbols in a file |
| Search for symbols across the workspace |
| Get compilation errors and warnings for a file or workspace |
All position-based tools use 0-based line and character offsets (LSP convention).
Project structure
jdtls-mcp/
├── agent.md # LLM agent onboarding guide
├── pom.xml # Tycho parent (mirrors eclipse-jdtls)
├── org.eclipse.jdt.ls.mcp.target/ # Target-platform definition
│ ├── pom.xml
│ └── org.eclipse.jdt.ls.mcp.tp.target # p2 + Maven deps (MCP SDK, langchain4j)
├── org.eclipse.jdt.ls.mcp/ # New OSGi eclipse-plugin bundle
│ ├── META-INF/MANIFEST.MF
│ ├── plugin.xml # Registers org.eclipse.jdt.ls.mcp.app
│ ├── build.properties
│ └── src/main/java/org/eclipse/jdt/ls/mcp/
│ ├── McpServerPlugin.java # BundleActivator
│ ├── McpApplication.java # Eclipse IApplication — MCP entry point
│ └── JdtlsMcpTools.java # @Tool methods + MCP tool registration
├── org.eclipse.jdt.ls.mcp.product/ # Eclipse product packaging
│ ├── pom.xml
│ └── jdtls-mcp.product # All jdtls bundles + org.eclipse.jdt.ls.mcp
└── test-workspace/hello-jdtls/ # Sample Maven project for manual testingPrerequisites
Tool | Version | Notes |
Java | 21+ | Must be on |
Installation
Option A — Download a release (recommended)
Download the archive for your platform from the Releases page, extract it, and you're ready to go — no build step required.
# Linux x86_64
curl -L https://github.com/sunix/jdtls-mcp/releases/latest/download/jdtls-mcp-linux-x86_64.tar.gz \
| tar xz
cd jdtls-mcp
# macOS arm64 (Apple Silicon)
curl -L https://github.com/sunix/jdtls-mcp/releases/latest/download/jdtls-mcp-macos-aarch64.tar.gz \
| tar xz
cd jdtls-mcpThe extracted directory contains plugins/, configuration/, and scripts/.
Option B — Build from source
Requires Maven 3.9+ in addition to Java 21.
git clone https://github.com/sunix/jdtls-mcp.git
cd jdtls-mcp
mvn package -DskipTestsThe built product lands at:
org.eclipse.jdt.ls.mcp.product/target/products/jdtls-mcp.product/
├── linux/gtk/x86_64/
├── linux/gtk/aarch64/
├── macosx/cocoa/x86_64/
├── macosx/cocoa/aarch64/
└── win32/win32/x86_64/Running
Use the bundled scripts/start-mcp-server.sh (included in release archives
and in the repo):
# Point at your Java workspace
./scripts/start-mcp-server.sh /path/to/your-java-project
# Optional second argument: Eclipse metadata/index directory
./scripts/start-mcp-server.sh /path/to/your-java-project /tmp/jdtls-dataThe script auto-detects OS and CPU architecture. The server reads MCP messages from stdin and writes JSON-RPC responses to stdout.
Startup takes~60 s on the first run while Maven imports the project and the JDT type-name index warms up. Subsequent starts against the same data directory are faster.
GitHub Copilot Coding Agent
Two things are needed: a one-time setup workflow (Java 21) and the MCP server configuration (download + start on each session).
Step 1 — Add the setup workflow
Add the following file to your Java repository (not this repo). It runs before every Copilot coding agent session and ensures Java 21 is on PATH.
.github/workflows/copilot-setup-steps.yml
on:
workflow_dispatch:
permissions:
contents: read
jobs:
copilot-setup-steps:
runs-on: ubuntu-latest
steps:
- name: Set up Java 21
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '21'Step 2 — Add the MCP server configuration
Go to GitHub Settings → Copilot → Coding agent → MCP configuration (or your organisation's equivalent) and paste the following JSON:
{
"mcpServers": {
"jdtls": {
"type": "local",
"command": "bash",
"args": [
"-c",
"f=$(mktemp /tmp/jdtls-mcp-XXXXXX.sh) && curl -fsSL https://raw.githubusercontent.com/sunix/jdtls-mcp/main/scripts/download-and-start.sh -o \"$f\" && chmod +x \"$f\" && exec \"$f\""
],
"tools": ["*"]
}
}
}On each session, download-and-start.sh fetches the latest release (or reuses
the cached copy at ~/.cache/jdtls-mcp/<version>/) and starts the MCP server
against $GITHUB_WORKSPACE. The mktemp pattern keeps stdin free for MCP
traffic while curl runs.
Startup takes~60 s on the first session while Maven imports the project and the JDT type-name index warms up. Subsequent sessions against the same data directory are faster.
Testing with MCP clients
The repository includes a ready-to-use sample Java project at
test-workspace/hello-jdtls/ — use it as the jdtls workspace when trying
any of the clients below.
export WORKSPACE="$PWD/test-workspace/hello-jdtls"The examples below assume you have either downloaded a release archive and
extracted it to ~/jdtls-mcp/, or cloned and built the repo. Adjust
JDTLS_MCP_DIR to wherever scripts/start-mcp-server.sh lives:
# Downloaded release (example)
export JDTLS_MCP_DIR="$HOME/jdtls-mcp"
# Built from source
export JDTLS_MCP_DIR="$PWD" # repo rootGitHub Copilot in VSCode
Requires: VSCode ≥ 1.99 with the GitHub Copilot extension (MCP support is enabled by default; no feature flag needed).
Add the MCP server by creating (or editing)
.vscode/mcp.jsonin your Java project folder:mkdir -p "$WORKSPACE/.vscode" cat > "$WORKSPACE/.vscode/mcp.json" << EOF { "servers": { "jdtls": { "type": "stdio", "command": "$JDTLS_MCP_DIR/scripts/start-mcp-server.sh", "args": ["$WORKSPACE"] } } } EOFOpen the project in VSCode:
code "$WORKSPACE"Start the MCP server — VSCode will prompt you to start MCP servers defined in
.vscode/mcp.jsonwhen you open Copilot Chat. Click Start next tojdtls.Open Copilot Chat (
Ctrl+Alt+I/Cmd+Alt+I) and try these prompts:Use the java_hover tool to show me the Javadoc for the greet() method in file:///path/to/test-workspace/hello-jdtls/src/main/java/com/example/Greeter.java at line 34. Use java_document_symbols to list all symbols in file:///path/to/test-workspace/hello-jdtls/src/main/java/com/example/Greeter.java Use java_workspace_symbols to find all classes named Greeter.
Claude Code
Requires: Claude Code CLI installed (
npm install -g @anthropic-ai/claude-codeor follow the official install guide).
Register the MCP server (run once):
claude mcp add jdtls -- "$JDTLS_MCP_DIR/scripts/start-mcp-server.sh" "$WORKSPACE"Verify it was registered:
claude mcp listOpen the project with Claude Code:
cd "$WORKSPACE" claudeTry these prompts inside the Claude Code REPL:
Show me the Javadoc for the greet() method in src/main/java/com/example/Greeter.java using the jdtls MCP tools. Find all references to the `name` field in Greeter.java. List all symbols defined in Greeter.java.Claude Code will automatically call the
java_hover,java_references, andjava_document_symbolstools and display the results inline.Remove the server when done:
claude mcp remove jdtls
GitHub Copilot CLI (gh copilot)
Requires: GitHub CLI with the Copilot extension.
gh extension install github/gh-copilot gh auth login # if not already authenticated
The GitHub Copilot CLI (gh copilot suggest / gh copilot explain) uses MCP
servers configured in the GitHub Copilot for CLI settings file.
Find or create the config file:
OS
Path
Linux / macOS
~/.config/gh-copilot/config.yaml(may vary by version)Add the MCP server configuration:
mkdir -p ~/.config/gh-copilot cat >> ~/.config/gh-copilot/config.yaml << EOF mcp: servers: jdtls: command: "$JDTLS_MCP_DIR/scripts/start-mcp-server.sh" args: - "$WORKSPACE" EOFAsk Copilot CLI a question about the Java project:
gh copilot suggest "What public methods does the Greeter class have? Use the jdtls MCP tools to check."Or use
explainmode:gh copilot explain "Use java_workspace_symbols to find all classes in the test-workspace"
MCP support in Copilot CLI is evolving. Checkgh copilot --help or the
GitHub Copilot CLI changelog
for the latest configuration options.
Contributing
TL;DR — Java 21, Maven 3.9+, then
mvn package -DskipTests. All commits must follow Conventional Commits.
Prerequisites
Tool | Version | Notes |
Java | 21+ | Must be on |
Maven | 3.9+ | Tycho wraps OSGi builds via Maven |
An internet connection is required on first build to resolve the Eclipse p2 target platform and Maven Central dependencies.
IDE setup
Eclipse IDE (recommended for OSGi/plugin development):
Install Eclipse IDE for Eclipse Committers (2024-12 or later).
Install Tycho Project Configurators via Help → Install New Software from the Eclipse release update site.
Import the project: File → Import → Maven → Existing Maven Projects, select the repo root.
Eclipse will automatically set up the target platform from
org.eclipse.jdt.ls.mcp.tp.target.
VS Code / IntelliJ also work for editing Java source; just run mvn package
from the terminal for builds.
Build
mvn package # full build (all platforms)
mvn package -DskipTests # skip test execution for faster iteration
mvn package -pl org.eclipse.jdt.ls.mcp.product -am # only the plugin + productTesting your changes locally
Two helper scripts are provided in scripts/:
Script | Purpose |
Start the server against any workspace — reads MCP from stdin, writes responses to stdout | |
Smoke-test all tools against |
Smoke-test all tools at once:
# Run from the repo root after 'mvn package -DskipTests'
./scripts/test-mcp.shThe script starts the server, sends the MCP handshake followed by one call for every tool, and prints the raw JSON-RPC responses to stdout. Startup takes around 60 s on first run while Maven imports the project and the JDT index warms up.
Start the server manually against any workspace:
# Against the bundled test project
./scripts/start-mcp-server.sh
# Against your own project (workspace dir, optional data/metadata dir)
./scripts/start-mcp-server.sh /path/to/my-java-project /tmp/jdtls-dataThe server then reads MCP messages from stdin and writes responses to stdout.
See agent.md for a complete self-testing workflow with example
JSON-RPC messages.
Wire-protocol quick test (no MCP client required)
The StdioServerTransportProvider from the MCP Java SDK uses
newline-delimited JSON (NDJSON) — one JSON object per line — not
the Content-Length framing used by LSP. You can therefore drive the server
entirely with shell here-docs or a file of JSON lines, or use the
scripts/test-mcp.sh script which handles this automatically.
Write MCP messages — one per line — and pipe them in:
cat > /tmp/mcp-input.txt << 'EOF'
{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}
{"jsonrpc":"2.0","method":"notifications/initialized","params":{}}
{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}
EOF
./scripts/start-mcp-server.sh 2>/dev/null < /tmp/mcp-input.txtThe server maps the workspace Maven project on first start; this takes
roughly 60 s. Send the messages immediately — theinitialize reply arrives as
soon as the transport is ready, and tool responses follow once project import
and JDT indexing finishes.
Example session — annotated server output
Below is a real session against test-workspace/hello-jdtls/.
Input (/tmp/mcp-input.txt) — three NDJSON lines:
{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}
{"jsonrpc":"2.0","method":"notifications/initialized","params":{}}
{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}Response line 1 — initialize reply:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"protocolVersion": "2024-11-05",
"capabilities": {
"logging": {},
"tools": { "listChanged": true }
},
"serverInfo": { "name": "jdtls-mcp", "version": "1.0.0" }
}
}Response line 2 — tools/list reply (formatted for readability):
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"tools": [
{
"name": "java_hover",
"description": "Get hover information (Javadoc, type info) for the Java symbol at a given position.",
"inputSchema": {
"type": "object",
"properties": {
"uri": { "type": "string", "description": "Absolute file URI, e.g. file:///path/to/MyClass.java" },
"line": { "type": "integer", "description": "0-based line number" },
"character": { "type": "integer", "description": "0-based character offset" }
},
"required": ["uri", "line", "character"]
}
},
{
"name": "java_definition",
"description": "Find the definition of the Java symbol (class, method, field) at the given position.",
"inputSchema": {
"type": "object",
"properties": {
"uri": { "type": "string", "description": "Absolute file URI, e.g. file:///path/to/MyClass.java" },
"line": { "type": "integer", "description": "0-based line number" },
"character": { "type": "integer", "description": "0-based character offset" }
},
"required": ["uri", "line", "character"]
}
},
{
"name": "java_references",
"description": "Find all references to the Java symbol at the given position.",
"inputSchema": {
"type": "object",
"properties": {
"uri": { "type": "string", "description": "Absolute file URI, e.g. file:///path/to/MyClass.java" },
"line": { "type": "integer", "description": "0-based line number" },
"character": { "type": "integer", "description": "0-based character offset" },
"includeDeclaration": { "type": "boolean", "description": "Whether to include the declaration itself" }
},
"required": ["uri", "line", "character", "includeDeclaration"]
}
},
{
"name": "java_completion",
"description": "Get code completion suggestions at the given position in a Java source file.",
"inputSchema": {
"type": "object",
"properties": {
"uri": { "type": "string", "description": "Absolute file URI, e.g. file:///path/to/MyClass.java" },
"line": { "type": "integer", "description": "0-based line number" },
"character": { "type": "integer", "description": "0-based character offset" }
},
"required": ["uri", "line", "character"]
}
},
{
"name": "java_document_symbols",
"description": "List all symbols (classes, methods, fields) defined in a Java source file.",
"inputSchema": {
"type": "object",
"properties": {
"uri": { "type": "string", "description": "Absolute file URI, e.g. file:///path/to/MyClass.java" }
},
"required": ["uri"]
}
},
{
"name": "java_workspace_symbols",
"description": "Search for Java symbols (classes, methods, fields) by name across the workspace.",
"inputSchema": {
"type": "object",
"properties": {
"query": { "type": "string", "description": "Search query, e.g. a class name or method name prefix" }
},
"required": ["query"]
}
}
]
}
}Example tool calls
java_workspace_symbols — find all classes named Greeter:
{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"java_workspace_symbols","arguments":{"query":"Greeter"}}}java_document_symbols — list all symbols in a file:
{"jsonrpc":"2.0","id":4,"method":"tools/call","params":{"name":"java_document_symbols","arguments":{"uri":"file:///root/github/sunix/jdtls-mcp/test-workspace/hello-jdtls/src/main/java/com/example/Greeter.java"}}}java_hover — get Javadoc at a specific position:
{"jsonrpc":"2.0","id":5,"method":"tools/call","params":{"name":"java_hover","arguments":{"uri":"file:///root/github/sunix/jdtls-mcp/test-workspace/hello-jdtls/src/main/java/com/example/Greeter.java","line":10,"character":18}}}java_definition — jump to the definition of a symbol:
{"jsonrpc":"2.0","id":6,"method":"tools/call","params":{"name":"java_definition","arguments":{"uri":"file:///root/github/sunix/jdtls-mcp/test-workspace/hello-jdtls/src/main/java/com/example/Greeter.java","line":10,"character":18}}}java_references — find all usages of a symbol:
{"jsonrpc":"2.0","id":7,"method":"tools/call","params":{"name":"java_references","arguments":{"uri":"file:///root/github/sunix/jdtls-mcp/test-workspace/hello-jdtls/src/main/java/com/example/Greeter.java","line":5,"character":13,"includeDeclaration":true}}}Example chat prompts for LLM agents
These prompts work well with GitHub Copilot, Claude Code, or any MCP-aware
LLM when the jdtls server is connected:
List all the methods and fields defined in
file:///…/test-workspace/hello-jdtls/src/main/java/com/example/Greeter.java
using the java_document_symbols tool.
Show me the Javadoc for the greet() method in Greeter.java using java_hover.
Find all classes in the workspace that contain the word "Counter" using
java_workspace_symbols.
Find every place in the codebase where the `name` field of Greeter is
referenced, using java_references.
Jump to the definition of the Counter class from the call site in Greeter.java
using java_definition.Adding a new MCP tool
Add a method annotated with
@Tool/@PinJdtlsMcpTools.java, delegating to the appropriate jdtls handler class (seeorg.eclipse.jdt.ls.core.internal.handlers).Register it in
JdtlsMcpTools.registerTools()using the existing.toolCall(tool(…), this::mcpXxx)pattern.Test it against
test-workspace/hello-jdtls/.Commit with
feat(tools): add java_<toolname> MCP tool.
Commit conventions
This project uses Conventional Commits:
<type>(<optional scope>): <short description>Type | When to use |
| New feature or new MCP tool |
| Bug fix |
| Code change without behaviour change |
| Adding or updating tests |
| Documentation only |
| Build config changes (pom.xml, target platform, …) |
| Maintenance (CI, .gitignore, …) |
Releasing
Releases are fully automated via the Release GitHub Actions workflow
(.github/workflows/release.yml). You never need to edit version numbers
manually or push a tag yourself.
What the workflow does automatically:
Runs
tycho-versions:set-versionto update all version strings atomically —pom.xmlfiles,MANIFEST.MF(Bundle-Version), andjdtls-mcp.product.Commits the version bump and creates the
v<version>git tag.Builds the product for all 5 platforms with Maven / Tycho.
Packages per-platform archives and publishes a GitHub Release with them.
Bumps versions to the next
-SNAPSHOT, commits, and pushes everything (tag + both commits) back tomain.
To cut a release:
Go to Actions → Release → Run workflow on GitHub.
Fill in Release version (e.g.
1.0.0). Optionally fill in Next development version (e.g.1.0.1-SNAPSHOT). Leave it blank to auto-increment the patch segment.Click Run workflow — that's it.
After the workflow completes:
A GitHub Release tagged
v1.0.0appears with the 5 platform archives.mainhas two new commits: the release version bump and the snapshot bump.
Branch protection note: if
mainhas push restrictions, grant thegithub-actions[bot]the bypass permission, or use a PAT stored asGH_RELEASE_TOKENand reference it in the workflow'stoken:field instead ofsecrets.GITHUB_TOKEN.
Key source files
File | Purpose |
| Eclipse |
| All MCP tool implementations + registration |
| OSGi |
| Registers |
| Target platform: jdtls p2 repo + MCP SDK + langchain4j |
| Lists all OSGi bundles for the packaged product |
| Sample Maven project for manual testing |
Licence
This server cannot be installed
Maintenance
Latest Blog Posts
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/sunix/jdtls-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server