关于 ariadne
ariadne 是面向 Kotlin 项目的 MCP 服务,可分析 Git 变更并筛选需要运行的单元测试,帮助编码 Agent 加快编辑验证循环,同时明确提示分析范围之外的变更。
主要功能
- 01筛选受 Kotlin 变更影响的测试
- 02自动读取 Git 差异
- 03支持深层与浅层变更范围
- 04返回排序后的测试全限定名
- 05提示分析范围外的变更
适用场景
- 01编辑 Kotlin 时运行针对性单元测试
- 02提交前检查分支变更
- 03判断是否应运行完整测试
MCP 相关
它向 MCP 客户端提供 `get_affected_tests` 工具。该工具接收 Kotlin 项目路径,并根据检测到的 Git 变更返回受影响的测试。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
docker run -i --rm ghcr.io/mikhailhal/ariadne:0.4.0本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。
ghcr.io/mikhailhal/ariadne:0.4.0stdio工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
ariadne
用于受影响测试选择的 MCP 服务
ariadne 是一个 MCP(Model Context Protocol)服务,可帮助 AI Agent 识别受影响的测试。它由 sazanami 提供支持,可分析代码更改并仅返回需要运行的测试。
重要
⚠️ 必须阅读:速度优先于完整性
ariadne 专为 Agent 的内循环而构建,即编辑、验证、提交;在此过程中,快速反馈比穷尽式选择更重要。静态分析无法追踪每一条执行路径:反射、DI 框架和数据流间接调用(例如 Flux/MVI 分发)可能会让任何受影响测试选择工具无法发现依赖关系,ariadne 也不例外。
始终在 CI 中保留最后一道防线。 合并前请运行完整测试套件(或采用保守的测试选择方案)。ariadne 会在 Agent 迭代期间缩小运行范围,但不能替代 CI。
当 ariadne 检测到无法分析的更改(构建脚本、资源、未扫描的源集)时,会在工具响应中明确说明,而不会悄悄报告“没有受影响的测试”。
功能
- MCP 集成 — 可与 Claude Code、Claude Desktop 及其他兼容 MCP 的客户端配合使用
- 自动 Git 差异 — 无需手动传递差异;ariadne 会在内部运行
git diff - 由 sazanami 提供支持 — 使用 Kotlin Analysis API 进行精准静态分析
安装
Homebrew(推荐)
brew install mikhailhal/tap/ariadne
然后在 MCP 客户端中注册;以 Claude Code 为例:
claude mcp add ariadne -- ariadne
或者手动将其添加到 MCP 客户端配置中(例如 Claude Desktop):
{
"mcpServers": {
"ariadne": {
"command": "ariadne"
}
}
}
Docker
docker run -i --rm -v /path/to/project:/workspace ghcr.io/mikhailhal/ariadne
挂载要分析的项目,并将 /workspace 作为 project_path 传入。
该镜像也已列入官方 MCP 注册表,名称为 io.github.MikhailHal/ariadne。
手动安装(发布的 JAR)
从 Releases 下载 ariadne-<version>-all.jar(需要 JDK 21+),并使用 "command": "java", "args": ["-jar", "/path/to/ariadne-<version>-all.jar"] 配置客户端。
从源代码构建
git clone --recursive https://github.com/MikhailHal/ariadne.git
cd ariadne
./gradlew shadowJar # fat JAR: build/libs/ariadne-<version>-all.jar
使用方法
配置完成后,AI Agent 可使用 get_affected_tests 工具:
工具:get_affected_tests
参数:
project_path(必填)— Kotlin 项目的路径scope(可选,deep|shallow,默认值为deep)— 回溯范围:deep—base_branch以来的所有更改(已提交 和 未提交)。最安全;覆盖整个分支,因此不会遗漏任何已提交但尚未验证的内容。可能会选择更多测试。shallow— 仅未提交的更改(与HEAD比较)。速度最快,适合紧凑的编辑循环。已提交工作的验证留给调用方。此时忽略base_branch。
base_branch(可选,仅适用于deep范围)— 用于比较的分支。省略时,ariadne 使用仓库的默认分支(origin/HEAD)。如果未设置该分支(例如没有远程仓库的仓库),则会返回错误,要求你显式传入base_branch,而不是自行猜测。
返回值:
- 受影响测试的 FQN(完全限定名称)列表,已排序
- 如果差异包含所分析 Kotlin 源码之外的更改(构建脚本、资源、未扫描的源码集),则会附加说明,建议针对这些更改运行完整测试
- 分析超时时间为 120 秒;超时会返回明确的错误
示例
Agent 请求:
{
"name": "get_affected_tests",
"arguments": {
"project_path": "/path/to/your/kotlin/project"
}
}
响应:
com.example.UserServiceTest.testCreateUser
com.example.UserRepositoryTest.testSave
工作原理
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ MCP Client │ ──▶ │ ariadne │ ──▶ │ sazanami │
│ (Agent) │ │ (MCP Server)│ │ (Analysis) │
└─────────────┘ └─────────────┘ └─────────────┘
│
▼
┌─────────────┐
│ git diff │
└─────────────┘
- Agent 调用工具 — 将项目路径传给 ariadne
- 运行 git diff — ariadne 针对基础分支执行
git diff --unified=0 - 使用 sazanami 分析 — 构建调用图并查找受影响的测试
- 返回结果 — 将测试 FQN 列表返回给 Agent
真实场景验证:Now in Android
在 Now in Android 上测得 (Google 的 Android 参考应用 — 34 个模块,约 268 个 Kotlin 文件):
| 指标 | 结果 |
|---|---|
| 召回率审计 — 所有层的 19 个目标函数 | 检测到 18/18 个有效目标(第 19 个没有测试它的单元测试;未选择它是正确的) |
| 端到端响应时间 | 约 4 秒(模块发现 + 调用图构建 + BFS) |
| 模块发现 | 通过 settings.gradle.kts 发现 34 个模块,包括嵌套模块和类型安全访问器依赖 |
| 源码集 | 按模块发现 main、debug、prod、benchmark、testDemo 等(按设计排除 androidTest*) |
已验证的模式包括:项目接口背后的仓库、库接口的覆盖实现(androidx.datastore.Serializer)、operator fun invoke 用例、@Composable 函数、扩展映射器、ViewModel 属性初始化器链,以及可调用引用。两个有代表性的结果:
- 更改
core:common的asResult()会选中三个模块中的 14 个测试, 包括只能通过val uiState = ...stateIn(...)访问到的 ViewModel 测试 - 更改映射器
PopulatedNewsResource.asExternalModel()会选中14 个测试, 包括只能通过.map(Type::mapper)链访问到的 11 个仓库测试
测试类选择率
通过更改每个单元测试类所测试类中的函数,并检查该测试类是否被选中,对 Now in Android 中的所有单元测试类进行了测量:
| 测试风格 | 已选中 |
|---|---|
| 普通单元测试(构造对象并调用它) | 13 / 13 个有效目标 |
| Robolectric / Compose 截图测试 | 12 / 12 |
由框架分发的回调(lint Detector) | 0 / 2 — 见下文 |
事实证明,Robolectric 并不是障碍:这些测试会自行调用可组合项(setContent { NiaTheme { ... } }),因此源码中存在该调用。决定覆盖情况的不是测试运行器,而是测试自身的代码是否包含该调用。
ariadne 无法看到的内容
经验法则:如果是框架调用你的代码,而不是测试调用你的代码,ariadne 就无法将它们关联起来。 这些是静态分析的局限,并非缺陷——请据此规划 CI 安全保障:
| 模式 | 状态 |
|---|---|
框架调用的回调——Fragment/Activity 生命周期(launchFragmentInContainer)、lint Detector 方法、Application.onCreate | 不追踪:测试中没有写出调用 |
| 反射 / DI 容器接线 | 不追踪 |
| UDF 分发(Flux/MVI) | dispatch → collect 不会形成边,但 init 中的接线(或测试调用的 start())会通过构造函数链得到保守覆盖。由 DI/生命周期启动的订阅不在覆盖范围内(sazanami#38) |
stateIn / shareIn 链(map、onEach、flatMapLatest、combine) | 已覆盖——已通过精确选择验证 |
插桩测试(androidTest*) | 按设计不在范围内 |
| 构建脚本、资源、未扫描的源集 | 不分析——工具响应中会明确报告 |
| 同一包中同名的顶层扩展函数 | 选择范围偏广(接收者类型不属于顶层 FQN)——方向上是安全的 |
KMP 源集(commonMain、expect/actual) | 已枚举,但解析质量未经验证(#1) |
完整审计说明:sazanami#29, sazanami#38。
要求
- JDK 21 或更高版本
- Git——用于检测差异
局限
- 模块发现基于约定:解析
settings.gradle(.kts)中的 include, 枚举src/<sourceSet>/{kotlin,java}布局,并读取构建文件中的project(":x")/ 类型安全访问器依赖。无法检测动态 include、projectDir重映射、自定义srcDirs,以及由约定插件注入的依赖——请参阅 #1 - 每次请求都会完整重建图(尚未提供缓存);分析上限为 120s
- 分析层面的缺口请参阅 ariadne 无法看到的内容
许可证
Copyright 2025 ariadne contributors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
完整文本请参阅 LICENSE。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。