23
0
0

JetBrains Index MCP 源码解析:让 AI 直接使用 IDE 的代码分析能力

2026-09-19
2026-10-07
文章摘要
|

一、它解决的到底是什么问题

在一个有一百多个模块的 Java/Kotlin 工程里,AI 不仅需要找到代码,还需要确认代码之间的关系:某个调用指向哪个重载方法、某个接口有哪些实现、某个公共方法的调用者分布在哪些模块。

grep 和 ripgrep 擅长文本匹配。它们可以找到 save,但不能独立判断这个 save 属于订单仓库还是图片缓存。AI 可以继续读声明、导入、类型和继承关系来判断,只是需要更多步骤。

这个插件把 JetBrains 已有的代码导航、索引、语言解析与重构能力包装成 MCP 工具。AI 可以直接请求“查这个方法的引用”,把一部分代码关系判断交给 IDE 完成。

它的价值因此不只是搜索速度,还包括减少无关上下文、降低同名误判、辅助影响分析。它也不能替代业务理解或保证发现所有运行时调用。

二、部署形态:服务器就在 IDE 进程内

这是一个 JetBrains 插件,内部嵌入 HTTP MCP 服务。通常不需要再部署独立后端、向量数据库或额外的代码索引服务。

AI 编程客户端
    │ MCP / JSON-RPC / HTTP
    ▼
IDE 进程内的 Ktor CIO 服务器
    │ 工具分发、项目选择、参数检查
    ▼
IntelliJ Platform + 对应语言插件
    │ PSI、索引、引用解析、重构 API
    ▼
IDE 已加载的工程、模块、依赖

2.1 安装与连接

  1. 在 IDE 插件市场安装 IDE Index MCP Server,按提示重启。

  2. 打开目标工程,等待 Gradle/Maven 导入和索引完成。

  3. 在 Settings → Tools → Index MCP Server 查看端口、工具开关等设置。

  4. 将工具窗口提供的 MCP URL 配置到客户端;也可以使用插件的 Install on Coding Agents 功能。

默认连接地址如下:

IDE

服务名

MCP URL

IntelliJ IDEA

intellij-index

http://127.0.0.1:29170/index-mcp/streamable-http

Android Studio

android-studio-index

http://127.0.0.1:29171/index-mcp/streamable-http

支持 mcpServers 和 url 配置格式的客户端可参考:

{
  "mcpServers": {
    "intellij-index": {
      "url": "http://127.0.0.1:29170/index-mcp/streamable-http"
    }
  }
}

不同客户端的配置文件和字段可能不同,应以插件生成的对应客户端配置为准。IDEA 和 Android Studio 是不同进程、不同服务;连接某个服务,不意味着能查询另一个 IDE 中的工程。

当前源码要求 IntelliJ Platform 2025.3 或更高版本,JVM 21 或更高版本。自行构建插件可使用 JDK 21 执行 ./gradlew buildPlugin,然后从 build/distributions/ 选择 ZIP,通过 IDE 的“从磁盘安装插件”安装。./gradlew runIde 用于启动开发沙箱。

默认监听 127.0.0.1,没有身份认证。源码有 Origin/Host 防护,但它不等于身份认证;如改成远程监听,需要另行处理访问控制。重构工具会修改源码。

2.2 源码中的启动过程

plugin.xml 注册 McpServerStartupActivity 为项目启动活动。打开项目时,它通知 ProjectResolver、初始化构建诊断缓存,然后调用 McpServerService.initialize()。

McpServerService 是应用级服务,即同一个 IDE 实例中的多个项目共享一个服务。它初始化工具注册表和语言处理器,读取设置并启动 KtorMcpServer,也负责停止、重启和异常停止后的恢复。

KtorMcpServer 使用 embeddedServer(CIO, port, host)。主要提供无状态 Streamable HTTP,同时保留旧版 SSE 接口。主要接口不要求客户端保存 Mcp-Session-Id。

注意:HTTP 传输无状态,不代表插件完全无状态。分页缓存、符号句柄、项目状态仍保存在 IDE 服务中,会受到服务重启和代码变化的影响。

三、一个 MCP 请求如何到达 IDEA

协议与业务分为两层:MCP SDK 负责 initialize、tools/list、tools/call 等协议行为;插件负责具体工具执行。

POST /index-mcp/streamable-http
  → MCP SDK 解析 JSON-RPC
  → McpServerFactory 注册的工具回调
  → McpToolDispatcher.call()
  → ProjectResolver 选择工程
  → AbstractMcpTool.execute()
  → FindClassTool / FindUsagesTool / CallHierarchyTool ...
  → IntelliJ API / 语言专用 Handler
  → 序列化结果返回客户端

McpServerFactory 为无状态 HTTP 请求创建 MCP Server 对象。注册工具时,会根据设置过滤禁用工具,因此 tools/list 不会展示所有源码中存在的功能。关闭工具后,直接调用也会被分发层拒绝。

McpToolDispatcher 主要做以下工作:

  • 确认工具存在、已启用,并检查 IDE UI 线程是否失去响应。

  • 根据 project_path 或符号句柄所属项目路由请求。

  • 记录命令历史、执行结果和耗时。

  • 在 IntelliJ 所需的执行上下文中运行工具。

  • 对普通工具设置默认 55 秒执行超时;部分长操作自行管理等待预算。

  • 将工具执行失败转成 isError: true,区别于 JSON-RPC 协议错误。

AbstractMcpTool 提供参数解析、目标定位、PSI 同步相关处理、索引状态检查和读写操作封装。具体工具通常在安全的读操作中访问 PSI;修改源码则需要相应的写操作机制。

整个过程直接调用 IDE 内部 API,不需要模拟鼠标点击、快捷键或读取屏幕。 “跳转定义”工具可以返回定义位置而不打开编辑器;显式打开文件是另一个工具的职责。

四、关键参数:先选工程,再选目标,再控制范围

4.1 选择工程

参数

含义

project_path

项目绝对路径。多个工程打开时建议明确传入;也支持可解析的模块内容根或项目子目录

只打开一个工程时通常可以省略。对于支持 symbolId 的工具,省略项目路径时也可以由句柄路由到所属工程。

跨模块与跨独立项目需要区分:同一 IDE 工程中正确导入的模块可以通过项目模型解析依赖;在多个窗口打开多个仓库,并不会自动把它们合成一个统一语义工程。

4.2 选择具体类或方法

许多语义导航工具支持以下三种方式,选择一种即可:

定位方式

参数

使用场景

源码位置

file、line、column

已知代码位置,直接解析该处符号

全限定符号

language、symbol

已知完整类名、方法名或签名

符号句柄

symbolId

使用前一个查询返回的精确目标

line、column 从 1 开始。项目文件一般传项目相对路径;只读导航也支持插件返回的部分依赖库绝对路径或 jar:// 地址。位置应指向目标名称,例如 repo.save() 中的 save。

Java 符号示例为 com.example.OrderService#submit(String)。符号字符串语法依赖语言处理器,不能把 Java 语法直接套到所有语言。

支持统一目标的工具也可以使用嵌套结构:

{
  "target": {
    "position": {
      "file": "order/src/main/java/com/example/OrderService.java",
      "line": 42,
      "column": 17
    }
  }
}

嵌套 target 与顶层的目标选择参数不要混用。最终以当前服务 tools/list 返回的 schema 为准。

4.3 控制范围和结果数量

参数

作用

scope

project_files、project_and_libraries、project_production_files、project_test_files

pageSize

列表查询每页数量;查类默认 25,查引用默认 100,通常最大 500

cursor

继续读取上一轮查询的后续结果,不应手工构造

paths

部分工具支持的项目相对路径 glob,如查引用可限定 order/**

includeGenerated

是否包含生成代码;不同工具默认值不同

当前版本中,查类默认排除生成代码;查引用、调用链默认包含生成代码。对于 Android 的 KSP、Dagger 等工程,这一点很重要:生成代码既可能制造噪声,也可能是有效引用的关键来源。

分页字段并不完全统一:列表查询通常返回 nextCursor,层级查询使用 cursor。需要根据具体响应继续请求,并关注 hasMore、stale、totalIsExact、truncationReason 等适用字段,不能只看当前页长度就断言结果完整。

五、查代码、查 class、查方法链路是如何实现的

这里需要区分两个概念:PSI(Program Structure Interface)是 IntelliJ 对程序结构的表示和访问接口,能表示类声明、方法、表达式与引用;索引则帮助 IDE 快速找到候选文件和符号。具体引用指向谁,还需要语言解析结合类型、作用域、导入和依赖关系确认。因此,它不是简单维护一张方法名列表,也不是依靠向量相似度检索代码。

5.1 查 class:复用 Go to Class 的索引入口

FindClassTool 使用 ChooseByNameContributor.CLASS_EP_NAME 扩展点搜索类与接口,并使用 IntelliJ 的名称匹配机制处理缩写、子串等查询。

典型参数为:

{
  "project_path": "/work/company-app",
  "query": "OrderService",
  "language": "Java",
  "matchMode": "exact",
  "scope": "project_files",
  "pageSize": 20
}

返回类名、全限定名称、文件位置、类型及可用的符号句柄等信息。matchMode 支持 substring、prefix、exact;默认是 substring。

这里的优势是查“类声明”,不会把注释里提到的同名文字一起当成类。

5.2 查定义:从引用解析到声明

FindDefinitionTool 先定位 PSI 元素,再通过 PsiUtils 等辅助逻辑解析实际目标。如果目标是依赖库,代码会尽量导航到源码,而不是停留在编译后的占位元素。

结果包含定义位置、名称、预览和 symbolId。定义预览并不等于读取完整文件,完整阅读可以接续文件读取工具。

symbolId 在服务中关联具体 PSI 目标,避免每一步都重新猜测行列或重载签名。它不是永久 ID,也不能靠比较 ID 字符串判断两个结果是否为同一声明。服务重启、项目关闭、目标删除、缓存淘汰等情况会使其失效,需要重新查询。

5.3 查源码和文件结构

工具

主要参数

用途

ide_read_file

file 或 qualifiedName,可加 startLine、endLine

阅读项目文件或可访问的依赖库内容

ide_file_structure

file,可加 includeNodes、includeSymbolIds

获取类、方法、字段等结构与位置

ide_open_file

以实际 schema 为准

在编辑器中打开文件,和返回代码文本是不同操作

这三个工具在该版本中默认禁用,需要在 Exposed Tools 中开启。文件结构默认返回文本树;设置 includeSymbolIds: true 会同时请求结构节点和句柄,可用于后续查方法引用。

{
  "project_path": "/work/company-app",
  "file": "order/src/main/java/com/example/OrderService.java",
  "includeNodes": true,
  "includeSymbolIds": true
}

对于大文件,可以先获取结构,再按节点位置读取局部源码,避免一次返回整份文件。依赖库可读内容取决于 IDE 能取得的源码或导航表示,不应假设任意 JAR 都带完整源代码。

5.4 查引用:按符号身份搜索

FindUsagesTool 的核心是 ReferencesSearch.search(targetElement, searchScope)。它不是搜索方法名字符串,而是向 IDE 请求目标元素的引用。

返回结果可包含文件、行列、上下文片段和引用类型。引用不仅包括方法调用,还可能是导入、字段访问等。因此“引用数量”和“调用者方法数量”不能直接比较。

{
  "project_path": "/work/company-app",
  "symbolId": "sym_REPLACE_WITH_REAL_METHOD_ID",
  "scope": "project_production_files",
  "pageSize": 50,
  "includeGenerated": true
}

做全工程影响分析时,不宜一开始就用 paths 排除其他业务模块。需要聚焦时再限定目录。

5.5 查实现和继承

LanguageHandlerRegistry 根据可用语言插件加载处理器。Java/Kotlin 的实现集中在 JavaHandlers.kt,其中使用:

  • ClassInheritorsSearch 查询类或接口的继承者。

  • OverridingMethodsSearch 查询方法的覆盖实现。

  • 父方法相关 API 查询方法继承关系。

对应工具包括 ide_find_implementations、ide_type_hierarchy、ide_find_super_methods。这解决了“实现藏在抽象父类或间接继承中”的查找问题,但列出实现不等于确定运行时依赖注入选择了哪个实现。

5.6 查调用链:向上找调用者,向下找被调用者

CallHierarchyTool 负责解析目标、参数和分页,再交给对应语言的调用关系处理器。

参数

含义

direction: "callers"

谁调用当前方法,适合向上追入口、分析影响

direction: "callees"

当前方法调用谁,适合向下追实现

depth

查询深度,默认 3,最大 5

maxNodes

设置后使用有界广度优先分页,范围 1–500

cursor

继续当前层级查询

Java/Kotlin 的调用者查询先把目标解析为 PsiMethod,再搜索当前方法及选取的最上层父方法的引用,从引用所在位置定位调用者方法,并继续向上查询。源码用去重和已访问集合避免重复展开。

这里还有一个具体的 Kotlin 适配:suspend 函数在 JVM 方法表示中存在编译器增加的 Continuation 参数,只使用 MethodReferencesSearch 可能漏掉源码调用。因此实现会对 Kotlin 声明补充 ReferencesSearch,然后去重。

向下查 Java 调用时,实现遍历方法体内的 PsiMethodCallExpression,调用 resolveMethod() 解析被调用的方法,再递归展开;Kotlin 则有对应解析处理。

这是一种静态调用关系分析。例如实现方法的调用者查询会考虑父接口方法,因此结果可能包含通过接口发起的潜在调用,不能直接断言这些调用在运行时一定落到该实现。

此外,语法树中的调用表达式不等于实际执行顺序。条件分支、延迟执行的 lambda、异步调度、事件总线、反射和跨进程调用,都需要进一步结合源码、配置或运行时证据判断。

六、完整调用示例:从类定位到业务链路

正常情况下,MCP 客户端负责初始化和消息封装。协议过程可以理解为:

initialize → notifications/initialized → tools/list → tools/call

下面是一次完整的 JSON-RPC 工具调用,请求 IDEA 查找类:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "ide_find_class",
    "arguments": {
      "project_path": "/work/company-app",
      "query": "OrderService",
      "pageSize": 20
    }
  }
}

后续工具复用这个外层结构,替换 name 和 arguments 即可。推荐顺序如下:

  1. 调用 ide_index_status,确认不是 Dumb Mode,即 IDE 没有处于限制索引访问的阶段。

  2. 调用 ide_find_class 找到类,核对全限定名称和所在模块。

  3. 调用 ide_file_structure 获取方法节点,或按准确源码位置调用 ide_find_definition,拿到目标方法的句柄。类句柄不能直接代替方法句柄。

  4. 对接口调用 ide_find_implementations,明确候选实现。

  5. 对目标方法调用 ide_call_hierarchy,向上追入口或向下追依赖。

  6. 用 ide_read_file 读取关键方法,确认业务条件和实际行为。

  7. 用 ide_find_references 补查直接使用位置,检查分页和截断标志。

例如对已经发现的方法查询上游调用:

{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": {
    "name": "ide_call_hierarchy",
    "arguments": {
      "project_path": "/work/company-app",
      "symbolId": "sym_REPLACE_WITH_REAL_METHOD_ID",
      "direction": "callers",
      "depth": 3,
      "maxNodes": 50,
      "scope": "project_files",
      "includeGenerated": true
    }
  }
}

需要向下追踪时,将 direction 改成 callees。第一页没有涵盖全部结果时,按返回的 cursor 继续请求。不要无条件把深度拉满:公共底层方法的上游很容易扩展成庞大的关系图。

七、一百多个模块的工程,实际收益在哪里

假设需求是:“订单提交逻辑增加登录校验,先查清影响范围。”以下是工作方式对比,不是性能实测。

工作环节

主要使用 grep/rg

结合 IDE MCP

找入口类

搜类名并识别声明

类索引返回候选声明

区分同名方法

读取类型、导入和上下文

IDE 解析到具体方法

找接口实现

搜名字,再追继承

查询类型和覆盖关系

追多层调用

多轮搜索与人工式判断

调用关系查询辅助逐层定位

确认业务影响

阅读关键业务代码

仍然需要阅读关键业务代码

重命名

修改并检查所有文本匹配

调用 IDE 重构能力更新可解析引用

最直接的收益,是把 AI 花在“这个名字到底指谁”的工作转移给 IDE。AI 仍然负责理解业务,但能更快取得相关证据。

准确性收益尤其体现在同名方法、重载、间接继承和跨语言引用上。不过插件自己也有适配代码和边界。例如当前 Java 调用者实现对父方法数量有限制,传统调用树也存在每层数量限制;因此不能把“使用语义 API”理解为“结果永远穷尽”。

八、能否省时间和 token

时间:少往返,但不是每条查询都更快

索引已就绪时,IDE 可以直接利用项目模型解析符号,减少 AI 反复搜索、读取文件、确认类型的步骤。多模块影响分析更容易受益。

但 IDE 同步和首次索引需要时间、CPU 与内存。大量引用或深调用树也可能很慢。源码中的列表分页扩展有时需要重新执行搜索并跳过已见结果,因此分页主要控制输出量,不保证底层检索成本同比下降。

Token:少读无关代码,而不是索引本身省 token

本地扫描文件或计算索引不会直接消耗模型 token。真正影响 token 的,是送给模型的工具说明、参数、返回内容以及后续推理与交互。

语义查询可能减少无关匹配和为确认类型而读取的上下文;分页、局部文件读取、结构节点也能控制输出。插件还支持把结构化响应从 JSON 转成 TOON,适合部分重复字段较多的数据,但具体收益取决于内容和分词。

另一方面,工具 schema、JSON 字段和大调用树都有开销。如果 rg 只需返回两行即可回答,MCP 不一定更省。本文没有进行相同任务的耗时与 token 对照测试,不给出节省百分比。

要评估公司工程中的收益,可以选择几个固定任务,以同一提交、相同问题分别执行两套流程,记录完成耗时、工具往返次数、token 用量,并人工核对遗漏与误判。只比较一次搜索耗时,不能代表完成任务的总成本。

九、使用中的关键边界

  1. 项目模型必须正确。 Gradle 同步失败、依赖缺失、模块未导入时,语义查询结果可能不完整。

  2. 磁盘代码与 IDE 缓存要一致。 外部工具编辑后,必要时调用 ide_sync_files。自动同步外部变化选项默认关闭,开启可能带来性能成本;构建文件变化还可能需要重新同步项目模型。

  3. 查不到不等于不存在。 先检查工具错误、搜索范围、索引状态、生成代码开关、分页及句柄有效性,再作判断。

  4. 语义查询不覆盖全部动态行为。 反射、依赖注入选择、事件分发、远程调用需要其他证据。

  5. 导航和重构能力不同。 查到引用不表示任意修改都能自动完成;使用重构工具后仍需检查 diff,并执行相关构建和测试。

  6. 工具可用性依赖版本和设置。 本地工具列表比网上 README 更能说明当前能调用什么,不同语言支持也不完全相同。

适合大型工程的组合是:用 rg 定位字符串、配置和日志;用 MCP 确认符号、继承和调用关系;用源码阅读解释业务;用构建、测试或运行时观测验证结论。

十、源码阅读索引

以下链接固定到本文分析提交,方便复核,避免后续 main 分支更新导致实现与文章不一致。

源码

建议关注的逻辑

gradle.properties

插件版本、IDE 平台版本

plugin.xml

启动入口和可选语言依赖

McpServerStartupActivity.kt

项目打开后的初始化

McpServerService.kt

应用级服务和服务器生命周期

KtorMcpServer.kt

HTTP 与 SSE 传输

McpServerFactory.kt

MCP 工具暴露和请求回调

McpToolDispatcher.kt

项目路由、超时、错误、历史

AbstractMcpTool.kt

参数、PSI、同步、读写封装

FindClassTool.kt

类名索引、匹配与分页

FindDefinitionTool.kt

定义解析和预览

FindUsagesTool.kt

符号引用搜索

CallHierarchyTool.kt

调用链目标、深度、广度优先分页

JavaHandlers.kt

Java/Kotlin 继承、实现、调用关系

USAGE.md

各工具完整参数与示例

本文完成的是源码和文档分析,没有对公司百模块工程执行查询,也没有运行插件构建、性能基准或 token 对照实验。

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或者给予支持!

JetBrains Index MCP 源码解析:让 AI 直接使用 IDE 的代码分析能力
/archives/jetbrains-index-mcp-source-analysis
作者
generals
发布于
2026-09-19
许可协议
CC BY-NC-SA 4.0