英语点点跟读
27.99MB · 2025-11-11
大家好,我是 方圆。本篇文章主要介绍 MCP Server Easy Code Reader,它可以帮助你在使用 Cursor/VSCode/IDEA 编写代码时,根据调用链路将多个项目或 Jar 包中相关的代码读取到上下文中,供 Code Agent 帮我们分析逻辑和编写代码,而无需再手动将源码复制到对话框中发送给 AI,提高 Code Agent 准确度和编码效率。MCP 已发布 Github 和 MCP.so,欢迎大家前往下载使用:
在介绍如何接入 Easy Code Reader 之前,我们先来看看它到底能做什么:
Easy Code Reader 特别适合与 Claude、ChatGPT 等大模型配合使用,接下来以 Copilot Code Agent 结合 Claude4.5 模型为例,介绍一些最佳实践:
在比较复杂的项目中一般会拆分多个微服务,某些功能的实现可能会跨多个项目调用,如果靠人梳理相关逻辑会比较耗时,所以可以将涉及的代码 clone 到本地后使用 Easy Code Reader MCP 并结合 Code Agent 进行分析。接下来我们以 Nacos 项目为例,假设我们想了解 Nacos 的服务注册功能是如何实现的,可以按照以下步骤操作:
首先,比如我们创建了一个 Nacos Client 客户端,在这段逻辑中执行服务注册:
public class Main {
private static final Logger logger = LoggerFactory.getLogger(Main.class);
public static void main(String[] args) throws NacosException, InterruptedException {
logger.info("开始初始化 Nacos 客户端...");
Properties properties = new Properties();
properties.put(PropertyKeyConst.SERVER_ADDR, "127.0.0.1:8848");
properties.put(PropertyKeyConst.NAMESPACE, "7430d8fe-99ce-4b20-866e-ed021a0652c9");
NamingService namingService = NacosFactory.createNamingService(properties);
System.out.println("=== 注册服务实例 ===");
try {
// 注册一个服务实例
namingService.registerInstance("test-service0", "127.0.0.1", 8080);
// 添加事件监听器
namingService.subscribe("test-service", event -> {
System.out.println("服务实例变化: " + event);
});
} catch (Exception e) {
System.out.println("服务注册失败(预期,因为服务器可能未启动): " + e.getMessage());
}
TimeUnit.HOURS.sleep(3);
}
}
因为我们创建 Nacos Client 执行服务注册时是由 Nacos 提供的 SDK 直接调用 NamingService#registerInstance 方法实现的,我们并不清楚底层是如何实现的,如果我们想要了解实现细节,那么可以将 Nacos 的源码 Clone 下来,并使用 Easy Code Reader 读取相关源码,下面是一个示例 Prompt:
你是一位 Java 专家,请你帮我分析 #file:Main.java 中 namingService.registerInstance 方法的逻辑,这段逻辑的实现在本地项目的 nacos 中,所以你需要在 nacos 读取一系列相关的源码才能了解它的核心逻辑,读取 nacos 项目的代码你可以借助 easy-code-reader MCP,其中包含你可以获取项目信息、项目中所有的文件信息和某个文件的工具
如图所示,它会不断地根据源码调用链路,读取相关源码并进行分析,最终我们就能了解服务注册的实现细节,会使用到 MCP Easy Code Reader 提供的多个工具 list_all_project、list_project_files 和 read_project_code, 具体调用细节图示如下:
最终得到分析结果,节省很多时间:
在使用第三方或其他外部依赖时,Copilot 或其他 Code Agent 并不能直接读取 jar 包中的源码,往往需要我们将源码内容手动复制到提示词中才能完成,费时费力。在 Easy Code Reader 中提供了 read_jar_source 工具来读取 jar 包中的源码,帮我们完成开发实现。我们还是以如下代码为例,现在我想实现多个服务实例的注册,但是我又不了解 NamingService 的实现,便可以借助 read_jar_source 来完成:
public class Main {
private static final Logger logger = LoggerFactory.getLogger(Main.class);
public static void main(String[] args) throws NacosException, InterruptedException {
logger.info("开始初始化 Nacos 客户端...");
Properties properties = new Properties();
properties.put(PropertyKeyConst.SERVER_ADDR, "127.0.0.1:8848");
properties.put(PropertyKeyConst.NAMESPACE, "7430d8fe-99ce-4b20-866e-ed021a0652c9");
NamingService namingService = NacosFactory.createNamingService(properties);
System.out.println("=== 注册服务实例 ===");
try {
// 注册一个服务实例
namingService.registerInstance("test-service0", "127.0.0.1", 8080);
// 添加事件监听器
namingService.subscribe("test-service", event -> {
System.out.println("服务实例变化: " + event);
});
// 注册多个服务实例
} catch (Exception e) {
System.out.println("服务注册失败(预期,因为服务器可能未启动): " + e.getMessage());
}
TimeUnit.HOURS.sleep(3);
}
}
你是一位 Java 技术专家,精通 Nacos 框架,请你帮我在 #file:Main.java 中完成注册多个服务实例的逻辑,在编写代码前,你需要先使用 easy-code-reader 的 read_jar_source 工具读取 com.alibaba.nacos.api.naming.NamingService 的源码信息来了解注册多个服务实例的方法
处理过程如下所示:
这样我们便能够快速地了解 NamingService 的实现细节,从而完成代码编写工作,节省了大量时间。
在大型项目中,某些功能的实现可能会跨多个模块或微服务,如果部分逻辑已经实现并且后续其他应用的逻辑需要依赖这部分逻辑时,可以借助 Easy Code Reader 读取相关模块的源码,帮助我们更好地理解和实现当前项目的功能,示例 Prompt 如下:
你是一位 Java 技术专家,现在我要实现 XXX 的业务逻辑,这部分逻辑的实现需要调用本地项目 A 中 XXX 的接口及其实现,请你借助 MCP easy-code-reader 来帮我读取 A 项目中的源码,并帮我实现 XXX 的业务逻辑
当然除了这三种应用场景以外,还可以使用 Easy Code Reader 完成以下事项:
read_jar_source 工具根据异常堆栈日志快速定位异常点read_jar_source 工具来完成新旧版本的实现差异,评估升级影响list_all_project、list_project_files 和 read_project_code,来分析新增的逻辑是否满足业务要求既然它有这么多的应用场景,那么我们该如何接入 Easy Code Reader 呢?下面我们来介绍一下接入步骤:
它的环境要求如下:
如果您还没有安装 uv,可以通过以下方式快速安装:
# macOS/Linux
curl -LsSf https://astral.sh/uv/install.sh | sh
# Windows
powershell -c "irm https://astral.sh/uv/install.ps1 | iex"
# 或使用 pip
pip install uv
或者参考 uv 官网 进行安装,并配置 uv 的安装路径添加到系统 PATH 中,以便可以直接使用 uvx 命令。uv 是一个极快的 Python 包和项目管理工具。使用 uvx 可以无需预先安装,直接运行,参考以下 MCP 客户端配置:
--maven-repo: 指定 Maven 仓库路径,将 /custom/path/to/maven/repository 内容替换为本地 Maven 仓库路径即可,不配置默认使用 MAVEN_HOME 目录或 ~/.m2/repository--project-dir: 指定本地项目目录路径,将 /path/to/projects 替换为实际保存所有项目的路径{
"mcpServers": {
"easy-code-reader": {
"command": "uvx",
"args": [
"easy-code-reader",
"--maven-repo",
"/custom/path/to/maven/repository",
"--project-dir",
"/path/to/projects"
],
"env": {}
}
}
}
将以上内容配置好后,AI 助手即可通过 MCP 协议调用 Easy Code Reader 提供的工具,完成多项目、多依赖的 Java 源代码读取工作。
如果使用 快速接入(方法一) 安装运行失败,那么可以采用直接安装到本地的方法,运行如下命令:
uv tool install easy-code-reader
安装成功后,执行以下命令获取安装目录:
which easy-code-reader
比如,输出结果是:/Users/fangyuan/.local/bin/easy-code-reader,那么需要按照如下方式配置 MCP 客户端:
{
"mcpServers": {
"easy-code-reader": {
"command": "/Users/fangyuan/.local/bin/easy-code-reader",
"args": [
"--maven-repo",
"/custom/path/to/maven/repository",
"--project-dir",
"/path/to/projects"
],
"env": {}
}
}
}
一般这样操作都能完成安装,后续如果有版本更新,可以通过以下命令进行升级:
uv tool install --upgrade easy-code-reader
uv 命令未找到,确保已正确安装 uv 并将其路径添加到系统 PATH 中,参考 快速接入(方法一),并尝试重启 IDE 后再启动 MCP Server。
以上内容就是 MCP Easy Code Reader 的介绍和接入方法,接下来的内容主要介绍一下它的实现原理,感兴趣的朋友可以继续往下看。
其实编写一个 MCP Server 并不复杂,大家可以参考这篇 Github 上的文章 Model Context Protocol(MCP) 编程极速入门。Easy Code Reader 通过 Python 语言实现,它提供了 4 个主要工具来协同完成源码读取工作:
list_all_project: 列出所有本地项目,这个工具能将配置的项目目录 --project-dir 下所有的文件夹都读取出来,每个文件夹代表一个项目,这样 Code Agent 便能根据项目名称来选择需要读取的项目list_project_files: 列出指定项目中的所有文件名,这个工具能将指定项目下的所有源码文件名都读取出来,Code Agent 便能根据调用链路选择需要读取的文件read_project_code: 读取指定项目中的某个文件源码,通过以上两个工具,Code Agent 能够定位到需要读取的文件,然后使用这个工具将源码内容读取出来,供 Code Agent 进行分析read_jar_source: 读取指定 jar 包中的某个类源码,如果调用链路中有外部 jar 包依赖的话,Code Agent 便可以使用这个工具将 jar 包中的源码读取出来,供分析和编写代码这四个工具协同工作,便能实现跨项目、多依赖的源码读取工作,帮助 Code Agent 更好地理解代码逻辑,从而提高代码编写效率和准确度,以下是 MCP Tool 实现的技术细节:
列举项目目录下所有的项目文件夹名称。
用途:
参数:
project_dir (可选): 项目目录路径,如未提供则使用启动时配置的路径project_name_pattern (可选): 项目名称模糊匹配模式(不区分大小写),用于过滤项目列表
nacos 将匹配包含 nacos、Nacos、NACOS 的项目名智能提示机制:
project_name_pattern 但未匹配到项目时,返回结果会包含提示信息project_name_pattern 参数重新查询示例 1 - 列出所有项目:
{}
示例 2 - 使用项目名称模糊匹配:
{
"project_name_pattern": "spring"
}
返回格式:
{
"project_dir": "/path/to/projects",
"project_name_pattern": "spring",
"total_projects": 2,
"projects": [
"spring-boot",
"spring-cloud-demo"
],
"hint": "已使用项目名称模式 'spring' 进行过滤。如果未找到预期的项目,可能是模式匹配过于严格。建议:不传入 project_name_pattern 参数重新调用 list_all_project 工具查看完整项目列表。",
"total_all_projects": 5
}
提示信息说明:
project_name_pattern 但未匹配到任何项目时,hint 字段会提示模式可能过于严格,并显示总项目数 total_all_projectsproject_name_pattern 且有匹配结果时,hint 字段会提醒如果结果不符合预期可以不传参数重新查询,同时显示总项目数列出 Java 项目中的源代码文件和配置文件路径。
用途:
支持两种模式:
sub_path):列出整个项目的所有文件sub_path):只列出指定子目录下的文件参数:
project_name (必需): 项目名称,例如 nacossub_path (可选): 指定项目内的子目录路径,例如 core 或 address/src/main/javafile_name_pattern (可选): 文件名模糊匹配模式(不区分大小写),用于进一步过滤文件列表
Service 将匹配包含 service、Service、SERVICE 的文件名project_dir (可选): 项目所在的父目录路径,如未提供则使用启动时配置的路径自动过滤内容:
src/test)、编译产物 (target, build)、IDE 配置、版本控制文件智能提示机制:
file_name_pattern 但未匹配到文件时,返回结果会包含提示信息file_name_pattern 参数重新查询示例 1 - 列出整个项目:
{
"project_name": "nacos"
}
示例 2 - 只列出 core 模块:
{
"project_name": "nacos",
"sub_path": "core"
}
示例 3 - 使用文件名模糊匹配:
{
"project_name": "nacos",
"file_name_pattern": "Service"
}
返回格式:
{
"project_name": "nacos",
"project_dir": "/path/to/projects/nacos",
"search_scope": "core",
"file_name_pattern": "Service",
"total_files": 15,
"files": [
"core/src/main/java/com/alibaba/nacos/core/service/NacosService.java",
"api/src/main/java/com/alibaba/nacos/api/naming/NamingService.java",
"..."
],
"hint": "已使用文件名模式 'Service' 进行过滤。如果未找到预期的文件,可能是模式匹配过于严格。建议:不传入 file_name_pattern 参数重新调用 list_project_files 工具查看完整文件列表。"
}
提示信息说明:
file_name_pattern 但未匹配到任何文件时,hint 字段会提示模式可能过于严格file_name_pattern 且有匹配结果时,hint 字段会提醒如果结果不符合预期可以不传参数重新查询从本地项目目录中读取指定文件的源代码或配置文件内容。
用途:
参数:
project_name (必需): 项目名称,例如 my-projectfile_path (必需): 文件标识符:可以是完全限定的 Java 类名或文件相对路径
com.example.MyClass (自动查找对应的 .java 文件)src/main/java/com/example/MyClass.javacore/src/main/java/com/example/MyClass.javasrc/main/resources/application.yml、pom.xmlREADME.md、docs/setup.mdproject_dir (可选): 项目目录路径,如未提供则使用启动时配置的路径支持的文件类型:
自动搜索路径:
src/main/java/{class_path}.java、src/{class_path}.java、{class_path}.javasrc/main/resources/、src/、config/ 及子模块推荐工作流程:
list_all_project 确认项目存在list_project_files(建议带 file_name_pattern 参数)查看文件列表示例 1 - 使用类名读取 Java 源代码:
{
"project_name": "my-spring-app",
"file_path": "com.example.service.UserService"
}
示例 2 - 使用相对路径读取 Java 文件:
{
"project_name": "nacos",
"file_path": "address/src/main/java/com/alibaba/nacos/address/component/AddressServerGeneratorManager.java"
}
示例 3 - 读取配置文件:
{
"project_name": "my-spring-app",
"file_path": "src/main/resources/application.yml"
}
示例 4 - 读取项目根目录的文件:
{
"project_name": "my-spring-app",
"file_path": "pom.xml"
}
返回格式:
{
"project_name": "my-spring-app",
"class_name": "com.example.service.UserService",
"file_path": "/path/to/projects/my-spring-app/src/main/java/com/example/service/UserService.java",
"code": "package com.example.service;nnimport ...nnpublic class UserService {n // ...n}"
}
从 Maven 依赖中读取 Java 类的源代码(优先从 sources jar,否则反编译)。
参数:
group_id (必需): Maven group ID,例如 org.springframeworkartifact_id (必需): Maven artifact ID,例如 spring-coreversion (必需): Maven version,例如 5.3.21class_name (必需): 完全限定的类名,例如 org.springframework.core.SpringVersionprefer_sources (可选,默认 true): 优先使用 sources jar 而不是反编译工作原理:
-sources.jar 中提取源代码(如果 prefer_sources=true)智能错误提示:
当 JAR 文件未找到时,工具会提供详细的排查建议:
read_project_code 工具读取项目的 pom.xml 文件<dependencies> 部分核对正确的 Maven 坐标这个智能提示机制特别适合与 AI 助手配合使用,能有效减少因 Maven 坐标错误导致的重复尝试。
示例:
{
"group_id": "org.springframework",
"artifact_id": "spring-core",
"version": "5.3.21",
"class_name": "org.springframework.core.SpringVersion"
}
返回格式:
{
"class_name": "org.springframework.core.SpringVersion",
"artifact": "org.springframework:spring-core:5.3.21",
"source_type": "sources.jar",
"code": "package org.springframework.core;nnpublic class SpringVersion {n // ...n}"
}
source_type 字段说明:
source_type 字段标识源码的来源,帮助 AI 助手了解代码的可靠性和新鲜度:
"sources.jar": 从 Maven 的 sources JAR 文件中提取(最可靠,与发布版本完全一致)"decompiled": 通过反编译器新反编译生成(可能存在反编译不完整的情况)"decompiled_cache": 从之前反编译的缓存中读取(避免重复反编译,提升性能)使用建议:
sources.jar 来源的代码最准确,可直接作为分析依据decompiled 来源的代码可能会有语法糖恢复、泛型擦除等反编译特征decompiled_cache 与 decompiled 质量相同,只是从缓存读取以提升效率read_jar_source 工具支持多个反编译器,并根据 Java 版本自动选择最合适的:
| Java 版本 | 推荐反编译器 | 说明 |
|---|---|---|
| 8 - 20 | CFR | 自动使用 CFR 反编译器(兼容 Java 8+),已包含在包中:src/easy_code_reader/decompilers/cfr.jar |
| 21+ | Fernflower | 自动使用 Fernflower 反编译器(IntelliJ IDEA 使用的反编译器),已包含在包中:src/easy_code_reader/decompilers/fernflower.jar |
反编译后的文件会被缓存在 JAR 包所在目录的 easy-code-reader/ 子目录中,例如:
如果 JAR 包位置为:
~/.m2/repository/org/springframework/spring-core/5.3.21/spring-core-5.3.21.jar
反编译后的源文件将存储在:
~/.m2/repository/org/springframework/spring-core/5.3.21/easy-code-reader/spring-core-5.3.21.jar
缓存文件本身也是一个 JAR 格式的压缩包,包含所有反编译后的 .java 文件,这样可以避免重复反编译相同的 JAR 包,提高性能。但 针对 SNAPSHOT 版本需要特殊处理: 因为 Maven 针对快照版本会生成带时间戳的 JAR(如 artifact-1.0.0-20251030.085053-1.jar),Easy Code Reader 会自动查找最新的带时间戳版本进行反编译,并且以缓存以 artifact-1.0.0-20251030.085053-1.jar 名称存储,提供版本判断的依据,当检测到新版本时,会自动清理旧的 SNAPSHOT 缓存,生成新的缓存文件。