pzalutski-pixel/javalens-mcp
GitHub: pzalutski-pixel/javalens-mcp
JavaLens 是一个基于 Eclipse JDT 的 MCP 服务器,为 AI Agent 提供媲美编译器的精准 Java 代码语义分析、导航与重构能力。
Stars: 34 | Forks: 12
# JavaLens:专为 AI 打造的 Java 代码分析工具
[](https://github.com/pzalutski-pixel/javalens-mcp/releases)
[](LICENSE)
[](https://openjdk.org/projects/jdk/21/)
一个提供 75 种 Java 语义分析工具的 MCP 服务器,直接基于 Eclipse JDT 构建,实现媲美编译器的精准代码理解。
## 专为 AI Agent 构建
JavaLens 的存在是因为 **AI 系统需要媲美编译器的精准洞察力,这是仅靠阅读源代码文件无法提供的**。当 AI 使用 `grep` 或 `Read` 来查找方法的使用情况时,它无法区分:
- 对一个方法的调用与对另一个不相关类中同名方法的调用
- 对字段的读取与对字段的写入
- 对接口的实现与不相关的类
- 转换为特定类型与其他对该类型的引用
这会导致错误的重构、遗漏用法以及对代码行为的不完整理解。
## 媲美编译器的精准分析
JavaLens 通过 Eclipse JDT(驱动 Eclipse IDE 的同一引擎)提供**媲美编译器的精准代码分析**。与文本搜索不同,JDT 能够理解:
- 跨继承层级的类型解析
- 方法重载与重写
- 泛型参数
- Import 解析与 classpath 依赖
- 从 1.1 版本到 **Java 25** 的 Java 源码(markdown Javadoc、模块导入、紧凑源文件、灵活构造函数主体)
- **Lombok** 生成的成员 —— 内置的 agent 使得 `@Data` 访问器等能够正确解析,因此使用它们的代码不会被标记为未定义
**示例:** 查找所有调用 `UserService.save()` 的位置:
| 方法 | 结果 |
|----------|--------|
| `grep "save("` | 返回 47 个匹配项,包括 `orderService.save()`、`saveButton`、注释 |
| `find_references` | 精确返回 12 次对 `UserService.save()` 的调用 |
## AI 训练偏差警告
AI 模型可能会表现出**对原生工具**(Grep、Read、LSP)的**训练偏差**,从而轻视 MCP 服务器工具,即使语义分析能提供更好的结果。发生这种情况是因为:
1. 训练数据中包含大量的 grep/文本搜索模式
2. 原生工具在模型的经验中“始终可用”
3. 模型可能无法识别语义分析何时更具优势
**为了获得最佳结果:**
在您的项目指令或系统提示词中添加引导(例如,针对 Claude Code 的 `CLAUDE.md`):
```
## Code Analysis 首选项
For Java code analysis, prefer JavaLens MCP tools over text search:
- Use `find_references` instead of grep for finding usages
- Use `find_implementations` instead of text search for implementations
- Use `analyze_type` to understand a class before modifying it
- Use refactoring tools (rename_symbol, extract_method) for safe changes
Semantic analysis from JDT is more accurate than text-based search,
especially for overloaded methods, inheritance, and generic types.
```
## 什么是 JavaLens?
JavaLens 是一个 MCP 服务器,赋予 AI 助手对 Java 代码库的深刻理解。它提供超越简单文本搜索的语义分析、导航、重构和代码智能工具。
## 为什么不使用 LSP?
Language Server Protocol 专为 IDE 自动补全和基础导航而设计——并不适合需要深度语义分析的 AI agent 工作流。
| 功能 | 原生 LSP | JavaLens |
|------------|------------|----------|
| 查找所有 `@Annotation` 的用法 | ❌ | ✅ |
| 查找所有 `new Type()` 的实例化 | ❌ | ✅ |
| 查找所有强制转换为特定类型的代码 | ❌ | ✅ |
| 区分对字段的读取与写入 | ❌ | ✅ |
| 检测循环包依赖 | ❌ | ✅ |
| 计算圈复杂度 | ❌ | ✅ |
| 查找未使用的私有方法 | ❌ | ✅ |
| 检测可能的空指针 bug | ❌ | ✅ |
| 从入口点进行项目范围内的死代码可达性分析 | ❌ | ✅ |
| 查找传递性执行某个符号的测试 | ❌ | ✅ |
JavaLens 通过 OSGi 直接封装了 **Eclipse JDT Core**,提供:
- **细粒度的引用类型**:专门查找强制转换、注解、throws 子句、catch 块、instanceof 检查、方法引用、类型参数
- **读写访问区分**:追踪字段在何处被修改或仅被读取
- **索引搜索**:JDT 在加载时预先构建索引,因此符号/引用查询无需遍历源文件
- **完整的 AST 访问**:直接进行操作以实现复杂重构
## 安装
### 前置条件
- **Java 21** 或更高版本(必须在 PATH 中或设置了 `JAVA_HOME`)——两种安装路径均需此条件。
- **Node.js 18+** ——*仅*当您使用下面提到的 npm/`npx` 安装路径时需要。如果您使用直接下载路径,请跳过。
JavaLens 是一个分析服务器,而不是编译器。它使用 Eclipse JDT 2025-12 来解析和理解从 **1.1 版本到 25** 的 Java 源代码。Java 21 仅作为服务器运行时需要。
### 从 GitHub Releases 安装(推荐 —— 仅限 Java)
如果您已经拥有 Java 21 并且没有 Node.js,这是最简单的途径。从 [Releases](https://github.com/pzalutski-pixel/javalens-mcp/releases) 下载:
| 平台 | 文件 |
|----------|------|
| 所有平台 | `javalens.zip` 或 `javalens.tar.gz` |
解压到您选择的位置(例如 `/opt/javalens` 或 `C:\javalens`)。然后让您的 MCP 客户端指向内置的 jar 文件——请参阅下方的[配置 MCP Client](#configure-mcp-client)。
### 通过 npm 安装(需要 Node.js 18+)
如果您已有 Node.js,`npx` 会在首次运行时下载并缓存 JavaLens 发行版(约 23 MB):
```
{
"mcpServers": {
"javalens": {
"command": "npx",
"args": ["-y", "javalens-mcp"],
"env": {
"JAVA_PROJECT_PATH": "/path/to/your/java/project"
}
}
}
}
```
### 配置 MCP Client
将其添加到您的 MCP 配置中(例如针对 Claude Code 的 `.mcp.json`):
```
{
"mcpServers": {
"javalens": {
"command": "java",
"args": ["-jar", "/path/to/javalens/javalens.jar", "-data", "/path/to/javalens-workspaces"]
}
}
}
```
`-data` 参数指定了 JavaLens 存储其工作空间元数据的位置。请参阅下方的[工作空间原理](#how-workspaces-work)。
### 自动加载项目
设置 `JAVA_PROJECT_PATH` 以在服务器启动时自动加载项目:
```
{
"mcpServers": {
"javalens": {
"command": "java",
"args": ["-jar", "/path/to/javalens/javalens.jar", "-data", "/path/to/javalens-workspaces"],
"env": {
"JAVA_PROJECT_PATH": "/path/to/your/java/project"
}
}
}
}
```
## 工作空间原理
与内存中的代码模型不同,Eclipse JDT 需要一个**工作空间目录**来存储:
- 用于快速符号查找的搜索索引
- 编译状态和缓存
- 项目元数据
### 工作空间位于源码之外
JavaLens 将其工作空间创建在**您的源项目之外**,以保持代码库的整洁:
```
Your Java Project (unchanged)
├── src/main/java/
├── pom.xml
└── (no Eclipse files added)
JavaLens Workspace (specified by -data)
└── {session-uuid}/
├── .metadata/ <- JDT indexes and state
└── javalens-project/ <- Links to your source (not copies)
```
**为什么这很重要:**
1. **无污染**:您的源代码树保持干净——没有 `.project` 或 `.classpath` 文件
2. **无冲突**:与任何构建系统协同工作,互不干扰
3. **会话隔离**:每个 MCP 会话都有自己独立的工作空间,可实现并发分析
### 会话生命周期
1. JavaLens 启动并创建一个唯一的工作空间:`{base}/{uuid}/`
2. `load_project` 创建指向您的源代码的链接文件夹
3. JDT 在工作空间(而非您的项目)中构建索引
4. 当会话结束时,工作空间将被清理
## 工具
### 导航(10 个工具)
| 工具 | 描述 |
|------|-------------|
| `search_symbols` | 通过 glob 模式搜索类型、方法、字段 |
| `go_to_definition` | 导航至符号定义 |
| `find_references` | 查找符号的所有用法 |
| `find_implementations` | 查找接口/类的实现 |
| `get_type_hierarchy` | 获取继承链 |
| `get_document_symbols` | 获取文件中的所有符号 |
| `get_symbol_info` | 获取指定位置的详细符号信息 |
| `get_type_at_position` | 获取光标处的类型详情 |
| `get_method_at_position` | 获取光标处的方法详情 |
| `get_field_at_position` | 获取光标处的字段详情 |
### 细粒度引用搜索(9 个工具)
这些工具使用 JDT 独有的引用类型常量——无法通过 LSP 实现:
| 工具 | 描述 |
|------|-------------|
| `find_annotation_usages` | 查找所有 `@Annotation` 的用法 |
| `find_type_instantiations` | 查找所有 `new Type()` 调用 |
| `find_casts` | 查找所有 `(Type) expr` 强制转换 |
| `find_instanceof_checks` | 查找所有 `x instanceof Type` 检查 |
| `find_throws_declarations` | 查找签名中的所有 `throws Exception` |
| `find_catch_blocks` | 查找所有 `catch(Exception e)` 块 |
| `find_method_references` | 查找所有 `Type::method` 表达式 |
| `find_type_arguments` | 查找所有 `List` 的用法 |
| `find_reflection_usage` | 查找 `Class.forName()`、`Method.invoke()` 及其他反射调用 |
### 分析(20 个工具)
| 工具 | 描述 |
|------|-------------|
| `get_diagnostics` | 获取编译错误和警告 |
| `validate_syntax` | 快速进行仅限语法的验证 |
| `get_call_hierarchy_incoming` | 查找方法的所有调用方 |
| `get_call_hierarchy_outgoing` | 查找方法调用的所有方法 |
| `find_field_writes` | 查找字段在何处被修改 |
| `find_tests` | 发现 JUnit/TestNG 测试方法 |
| `find_unused_code` | 查找未使用的私有成员 |
| `find_unreachable_code` | 项目范围的死代码 —— 在整个程序的调用图上,从任何 main 方法或测试均无法到达的成员 |
| `find_affected_tests` | 直接或传递性地执行某个符号的测试 —— 修改后需要运行的测试集合 |
| `find_possible_bugs` | 检测空值风险、空的 catch 块、资源泄漏 |
| `get_hover_info` | 获取符号的文档/签名 |
| `get_javadoc` | 获取解析后的 Javadoc |
| `get_signature_help` | 在调用点获取方法签名 |
| `get_enclosing_element` | 获取指定位置包含的方法/类 |
| `analyze_change_impact` | 影响范围 —— 按深度划分的直接调用点,或项目图上的完整传递闭包(`transitive=true`) |
| `analyze_data_flow` | 方法内的变量读/写/声明追踪;可选的 `followCalls` 可跨调用追踪至 sink 的空值/污点事实 |
| `analyze_control_flow` | 分支、循环、return/throw 点、嵌套深度 |
| `get_di_registrations` | 查找 Spring DI 注册(@Component、@Bean、@Autowired、@Inject) |
| `get_jpa_model` | 组装的 JPA 实体模型 —— 表、ID 字段、关系及其解析的目标和 mappedBy 端 |
| `get_http_endpoints` | 组装的 HTTP 路由表 —— 由类前缀组成的 Spring 和 JAX-RS 路径,映射到处理程序方法 |
### 复合分析(4 个工具)
结合多个查询以减少往返次数:
| 工具 | 描述 |
|------|-------------|
| `analyze_file` | 在一次调用中获取导入、类型、诊断信息 |
| `analyze_type` | 获取成员、层级结构、用法和诊断信息 |
| `analyze_method` | 获取签名、调用方、被调用方、重写方法 |
| `get_type_usage_summary` | 获取实例化、强制转换、instanceof 的次数 |
### 重构(16 个工具)
所有重构工具均返回**文本编辑**(以及重构创建新文件时的文件内容),而不是直接应用更改:
| 工具 | 描述 |
|------|-------------|
| `rename_symbol` | 跨整个项目重命名 |
| `organize_imports` | 排序和清理导入 |
| `extract_variable` | 将表达式提取为局部变量 |
| `extract_method` | 将代码块提取为新方法 |
| `extract_constant` | 提取为 `static final` 字段 |
| `extract_interface` | 从类方法创建接口 |
| `extract_superclass` | 将成员移动到新创建的父类中 |
| `inline_variable` | 用其初始化程序替换变量 |
| `inline_method` | 用方法体替换调用 |
| `change_method_signature` | 修改参数/返回值,更新所有调用方 |
| `convert_anonymous_to_lambda` | 将匿名类转换为 lambda 表达式 |
| `encapsulate_field` | 生成访问器并重写所有直接的字段访问 |
| `pull_up` | 将成员上移至父类 |
| `push_down` | 将成员下移至子类 |
| `introduce_parameter_object` | 将方法的参数打包到一个新类中,更新调用方 |
| `move_type_to_new_file` | 将嵌套类型移动到其独立的顶级文件中 |
### 快修复(5 个工具)
| 工具 | 描述 |
|------|-------------|
| `suggest_imports` | 查找未解析类型的导入候选项 |
| `get_quick_fixes` | 列出指定位置问题的可用修复方案 |
| `apply_quick_fix` | 通过 ID 应用修复(添加导入、移除导入、添加 throws、try-catch) |
| `apply_cleanup` | 应用 10 种 JDT 清理操作之一(转换循环、模式匹配、switch 表达式、文本块等)并返回重写后的源码 |
| `diagnose_and_fix` | 诊断文件并在一次调用中返回每个问题的首选快速修复编辑 |
### 指标(5 个工具)
| 工具 | 描述 |
|------|-------------|
| `get_complexity_metrics` | 圈/认知复杂度,每个方法的 LOC(代码行数) |
| `get_dependency_graph` | 作为节点和边的包/类型依赖关系 |
| `find_circular_dependencies` | 使用 Tarjan 的 SCC 算法检测包循环 |
| `find_large_classes` | 查找超过方法/字段/行数阈值的类型 |
| `find_naming_violations` | 根据标准 Java 命名规范进行检查 |
### 项目与基础设施(6 个工具)
| 工具 | 描述 |
|------|-------------|
| `health_check` | 服务器状态和功能 |
| `load_project` | 加载 Maven/Gradle/Bazel/纯 Java 项目 |
| `get_project_structure` | 获取包层级结构 |
| `get_classpath_info` | 获取 classpath 条目 |
| `get_type_members` | 按类型名称获取成员 |
| `get_super_method` | 在父类中查找被重写的方法 |
## 用法
### 基本工作流
```
1. load_project(projectPath="/path/to/java/project")
2. search_symbols(query="*Service", kind="Class")
3. find_references(filePath="...", line=10, column=15)
4. analyze_type(typeName="com.example.UserService")
```
### 坐标系统
所有行/列参数均为**从零开始索引**(zero-based):
- 第 0 行,第 0 列 = 文件的第一个字符
### 路径处理
- 默认情况下,响应路径为**相对路径**
- 所有路径均使用**正斜杠**(`/`)以保持跨平台一致性
- 输入路径可以是相对路径或绝对路径
## 重要说明
### 磁盘同步
每一个答案都会在**查询时根据磁盘上的文件进行验证**。在任何工具逻辑运行之前,JavaLens 会对已知的源文件进行内容哈希比对,检测编辑、添加和删除操作(agent 无需报告任何内容——服务器会自行发现更改),准确修复发生变更的内容,等待搜索索引吸收修复内容,然后才予以回答。没有文件监视器,也没有后台线程——验证是在 agent 发出的查询内部同步进行的,因此不会出现竞态条件。
**Agent 的循环仅仅是:编辑 → 查询。**
```
1. Use JavaLens tools to analyze
2. Write changes to files
3. Use JavaLens tools to verify — answers already reflect the changes
```
`load_project` 仅在首次使用时、当响应报告 `RELOAD_REQUIRED`(像 `pom.xml` 这样的构建文件已更改,因此必须重建 classpath)时,或者需要从头开始重建所有内容时才需要。如果验证本身失败,查询将返回 `VERIFICATION_FAILED`,而不是未经验证的答案。
**成本:**验证是基于哈希且并行执行的——针对每次查询的测量结果为:72 个文件的项目约需 2 毫秒,1,000 个文件约需 25 毫秒,10,000 个文件约需 180 毫秒。修复仅需花费与更改部分相当的时间(单个已编辑文件的调和耗时远不到一秒钟),绝不会进行完整的重新索引。
**手动模式:**设置 `JAVALENS_DISK_SYNC=manual` 以恢复 1.5.0 之前的契约——答案反映最后一次加载的状态,agent 在编辑文件后调用 `load_project`。工具描述和 MCP 的 `instructions` 字段始终声明当前生效的契约,并且 `health_check` 会将其报告为 `diskSync`。
### 重构返回编辑内容
重构工具返回文本编辑内容,但不修改文件。这提供了在应用更改之前对将要发生的变化的可见性。
### 会话隔离
每个 MCP 会话都是独立的,拥有自己的工作空间 UUID。多个会话可以并发分析同一个项目。
### 构建系统支持
JavaLens 加载三种真实的构建系统以及纯 Java 目录。每一种都会在 CI 中针对合成的、具有真实结构的测试夹具(具有跨模块依赖的多模块 reactor、真实的外部库、注解处理器)进行端到端测试。
| 系统 | 检测方式 | 单模块 | 多模块/多项目 | 从构建文件获取编译器合规级别 | 生成的源码 | 注解处理器 |
|--------|-----------|:-:|:-:|:-:|:-:|:-:|
| Maven | `pom.xml` | ✅ | ✅(reactor classpath 聚合,跨模块导航) | ✅ (`maven.compiler.release`/`source`/`target`) | ✅ (`target/generated-sources/*`) | ✅(跨整个 reactor 的 ``) |
| Gradle | `build.gradle` / `build.gradle.kts` | ✅ | ✅(解析 `settings.gradle include`;合并各子项目的 classpath) | ✅ (`sourceCompatibility`) | ✅ (`build/generated/sources//main/java`) | ✅ (`annotationProcessor` 配置) |
| Bazel | `MODULE.bazel` / `WORKSPACE.bazel` / `WORKSPACE` | ✅ | ✅(扫描每个 `BUILD.bazel` 包以查找源码;`bazel-bin` ↔ `bazel-out` 符号链接去重) | ✅(跨 `BUILD.bazel` 文件解析 `javacopts` 的 `-source`/`-target`/`--release`) | 不适用(Bazel 操作写入 `bazel-bin/`,而不是 `target/generated-sources/`) | ✅(任何包含 `META-INF/services/javax.annotation.processing.Processor` 的 classpath jar 都会自动注册) |
| 纯 Java | `src/` 目录 | ✅ | 不适用 | ✅(没有构建文件时回退到 `Runtime.version().feature()`) | 不适用 | 不适用 |
在项目加载期间会发生 `mvn` / `gradle` 的子进程调用。如果工具缺失或失败,JavaLens 会在 `load_project` 响应中显示结构化的 `LoadWarning`(例如 `MAVEN_SUBPROCESS_FAILED`、`GRADLE_SUBPROCESS_FAILED`、`COMPLIANCE_LEVEL_UNKNOWN`),以便调用者知道分析质量已下降,而不是默默地得到一个空的 classpath。
## 配置
| 环境变量 | 描述 | 默认值 |
|---------------------|-------------|---------|
| `JAVA_PROJECT_PATH` | 启动时自动加载项目 | (无) |
| `JAVALENS_TIMEOUT_SECONDS` | 操作超时时间 | 30 |
| `JAVALENS_DISK_SYNC` | `strict`(每个答案都根据磁盘验证)或 `manual`(agent 在编辑后调用 `load_project`) | strict |
| `JAVALENS_LOG_LEVEL` | TRACE/DEBUG/INFO/WARN/ERROR | INFO |
| `JAVA_TOOL_OPTIONS` | JVM 选项,例如针对大型项目设置 `-Xmx2g` | (默认:通过 eclipse.ini 设置为 512m) |
| `JAVALENS_LOMBOK_JAR` | 启动时附加的 Lombok agent jar 路径;覆盖内置的 jar | (内置) |
## 从源码构建
```
git clone https://github.com/pzalutski-pixel/javalens-mcp.git
cd javalens-mcp
./mvnw clean verify
```
发行版输出到 `org.javalens.product/target/products/`。
### 构建要求
- Java 21+(服务器运行时)
- Maven 3.9+(包含 wrapper 作为 `./mvnw`)
要运行**完整测试套件**(包括针对真实 Maven、Gradle 和 Bazel 构建的端到端测试),相应的工具也必须位于 `PATH` 中:
- Maven(由 wrapper 提供)
- Gradle 8+
- Bazel 9+(或 `bazelisk`)
当开发机器上缺少某个工具时,测试会优雅地跳过。设置 `JAVALENS_TESTS_REQUIRE_TOOLS=true` 可改变此门槛:缺少工具将导致硬失败,而不是跳过。CI 在运行时会设置此标志,因此任何配置缺失都会作为真实的失败显现出来,而不是默默地削弱测试套件。
### 测试
```
# Full suite,温和模式(缺少 tools 时跳过)
./mvnw verify
# Full suite,严格模式(缺少 tools 时失败;即 CI 的行为)
JAVALENS_TESTS_REQUIRE_TOOLS=true ./mvnw verify
```
构建系统覆盖率的结构设计为重点针对特定 bug 的测试加上真实的端到端测试。端到端测试为每个构建系统加载一个具有代表性的单一项目,一次性验证所有修复内容——具有 Lombok APT 和跨模块引用的多模块 Maven;具有注解处理器的多项目 Gradle;具有跨目标依赖的多目标 Bazel。CI 会在 Linux、macOS 和 Windows 上运行它们。
## 架构
```
flowchart TD
Client["MCP Client"]
MCP["org.javalens.mcp
McpProtocolHandler → ToolRegistry → 75 Tools"] Core["org.javalens.core
JdtServiceImpl → WorkspaceManager, SearchService"] JDT["Eclipse JDT Core (via OSGi / Equinox)
IWorkspace, IJavaProject, SearchEngine, ASTParser"] Client -->|"JSON-RPC over stdio"| MCP MCP --> Core Core --> JDT ``` ## 许可证 MIT 许可证 - 详见 [LICENSE](LICENSE)。
McpProtocolHandler → ToolRegistry → 75 Tools"] Core["org.javalens.core
JdtServiceImpl → WorkspaceManager, SearchService"] JDT["Eclipse JDT Core (via OSGi / Equinox)
IWorkspace, IJavaProject, SearchEngine, ASTParser"] Client -->|"JSON-RPC over stdio"| MCP MCP --> Core Core --> JDT ``` ## 许可证 MIT 许可证 - 详见 [LICENSE](LICENSE)。
标签:AI智能体, Eclipse JDT, MCP Server, MITM代理, SOC Prime, 代码分析, 代码重构, 凭证管理, 域名枚举, 开发工具, 暗色界面