pzalutski-pixel/javalens-mcp

GitHub: pzalutski-pixel/javalens-mcp

JavaLens 是一个基于 Eclipse JDT 的 MCP 服务器,为 AI Agent 提供媲美编译器的精准 Java 代码语义分析、导航与重构能力。

Stars: 34 | Forks: 12

# JavaLens:专为 AI 打造的 Java 代码分析工具 [![GitHub release](https://img.shields.io/github/v/release/pzalutski-pixel/javalens-mcp)](https://github.com/pzalutski-pixel/javalens-mcp/releases) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) [![Java 21](https://img.shields.io/badge/Java-21-orange.svg)](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)。
标签:AI智能体, Eclipse JDT, MCP Server, MITM代理, SOC Prime, 代码分析, 代码重构, 凭证管理, 域名枚举, 开发工具, 暗色界面