ariadne is an MCP (Model Context Protocol) server that provides AI agents with the ability to identify affected tests.
claude mcp add ariadne -- docker run -i --rm /path/to/project{
"mcpServers": {
"ariadne": {
"command": "docker",
"args": ["run", "-i", "--rm", "/path/to/project"]
}
}
}MCP Servers overview
<p align="center">
<img src="doc/ariadne-banner.svg" alt="ariadne banner" width="100%">
</p>
<p align="center">
<h1 align="center">ariadne</h1>
<p align="center">
<strong>MCP Server for Affected Test Selection</strong>
</p>
<p align="center">
<a href="https://github.com/MikhailHal/ariadne/releases"><img src="https://img.shields.io/github/v/release/MikhailHal/ariadne?style=flat-square&color=success" alt="Release"></a>
<a href="https://registry.modelcontextprotocol.io/v0/servers?search=io.github.MikhailHal/ariadne"><img src="https://img.shields.io/badge/MCP%20Registry-listed-6E56CF.svg?style=flat-square" alt="MCP Registry"></a>
<a href="https://github.com/MikhailHal/homebrew-tap"><img src="https://img.shields.io/badge/homebrew-mikhailhal%2Ftap-FBB040.svg?style=flat-square&logo=homebrew&logoColor=white" alt="Homebrew"></a>
<a href="https://github.com/MikhailHal/ariadne/pkgs/container/ariadne"><img src="https://img.shields.io/badge/ghcr.io-ariadne-2496ED.svg?style=flat-square&logo=docker&logoColor=white" alt="Container image"></a>
<a href="LICENSE"><img src="https://img.shields.io/badge/License-Apache%202.0-blue.svg?style=flat-square" alt="License"></a>
<a href="https://kotlinlang.org"><img src="https://img.shields.io/badge/Kotlin-2.3.0-7F52FF.svg?style=flat-square&logo=kotlin&logoColor=white" alt="Kotlin"></a>
</p>
</p>
<br>
**ariadne** is an MCP (Model Context Protocol) server that provides AI agents with the ability to identify affected tests. Powered by [sazanami](https://github.com/MikhailHal/sazanami), it analyzes code changes and returns only the tests that need to be run.
<p align="center">
<a href="https://glama.ai/mcp/servers/MikhailHal/ariadne">
<img src="https://glama.ai/mcp/servers/MikhailHal/ariadne/badges/card.svg" alt="ariadne MCP server" />
</a>
</p>
> [!IMPORTANT]
> ## ⚠️ MUST READ: Speed over Completeness
>
> ariadne is built for the agent inner loop — edit, verify, commit — where fast
> feedback matters more than exhaustive selection. Static analysis cannot trace
> every execution path: reflection, DI frameworks, and data-flow indirection
> (e.g., Flux/MVI dispatch) can hide dependencies from **any**
> affected-test-selection tool, not just ariadne.
>
> **Always keep a final line of defense in CI.** Run the full test suite (or a
> conservative selection) before merging. ariadne narrows what an agent runs
> while iterating; it is not a replacement for CI.
>
> When ariadne detects changes it cannot analyze (build scripts, resources,
> unscanned source sets), it says so explicitly in the tool response instead of
> silently reporting "no affected tests".
## Features
- **MCP Integration** — Works with Claude Code, Claude Desktop, and other MCP-compatible clients
- **Automatic Git Diff** — No need to pass diff manually; ariadne runs `git diff` internally
- **Powered by sazanami** — Uses Kotlin Analysis API for accurate static analysis
## Installation
### Homebrew (recommended)
```bash
brew install mikhailhal/tap/ariadne
```
Then register it with your MCP client — for Claude Code:
```bash
claude mcp add ariadne -- ariadne
```
Or add to your MCP client configuration manually (e.g., Claude Desktop):
```json
{
"mcpServers": {
"ariadne": {
"command": "ariadne"
}
}
}
```
### Docker
```bash
docker run -i --rm -v /path/to/project:/workspace ghcr.io/mikhailhal/ariadne
```
Mount the project you want analyzed and pass `/workspace` as `project_path`.
The image is also listed in the [official MCP Registry](https://registry.modelcontextprotocol.io/v0/servers?search=io.github.MikhailHal/ariadne) as `io.github.MikhailHal/ariadne`.
### Manual (release JAR)
Download `ariadne-<version>-all.jar` from [Releases](https://github.com/MikhailHal/ariadne/releases) (requires JDK 21+) and configure your client with `"command": "java", "args": ["-jar", "/path/to/ariadne-<version>-all.jar"]`.
### Build from Source
```bash
git clone --recursive https://github.com/MikhailHal/ariadne.git
cd ariadne
./gradlew shadowJar # fat JAR: build/libs/ariadne-<version>-all.jar
```
## Usage
Once configured, AI agents can use the `get_affected_tests` tool:
### Tool: `get_affected_tests`
**Parameters:**
- `project_path` (required) — Path to the Kotlin project
- `scope` (optional, `deep` | `shallow`, default `deep`) — how far back to look:
- **`deep`** — all changes since `base_branch` (committed *and* uncommitted). Safest; the whole branch is covered so nothing you already committed slips through unverified. May select more tests.
- **`shallow`** — uncommitted changes only (diff against `HEAD`). Fastest, for the tight edit loop. Verifying already-committed work is left to the caller. `base_branch` is ignored.
- `base_branch` (optional, `deep` scope only) — Branch to compare against. When omitted, ariadne uses the repository's default branch (`origin/HEAD`). If that is not set (e.g. a repo with no remote), it returns an error asking you to pass `base_branch` explicitly rather than guessing.
**Returns:**
- List of affected test FQNs (fully qualified names), sorted
- If the diff contains changes outside the analyzed Kotlin sources (build scripts,
resources, unscanned source sets), a note is appended recommending a full test run
for those changes
- Analysis is bounded by a 120s timeout; on timeout an explicit error is returned
### Example
Agent request:
```json
{
"name": "get_affected_tests",
"arguments": {
"project_path": "/path/to/your/kotlin/project"
}
}
```
Response:
```
com.example.UserServiceTest.testCreateUser
com.example.UserRepositoryTest.testSave
```
## How It Works
```
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ MCP Client │ ──▶ │ ariadne │ ──▶ │ sazanami │
│ (Agent) │ │ (MCP Server)│ │ (Analysis) │
└─────────────┘ └─────────────┘ └─────────────┘
│
▼
┌─────────────┐
│ git diff │
└─────────────┘
```
1. **Agent calls tool** — Passes project path to ariadne
2. **Run git diff** — ariadne executes `git diff --unified=0` against base branch
3. **Analyze with sazanami** — Build call graph and find affected tests
4. **Return results** — List of test FQNs returned to agent
## Real-World Validation: Now in Android
Measured against [Now in Android](https://github.com/android/nowinandroid)
(Google's reference Android app — 34 modules, ~268 Kotlin files):
| Metric | Result |
|---|---|
| Recall audit — 19 target functions across all layers | **18/18 valid targets detected** (the 19th had no exercising unit test; correctly not selected) |
| End-to-end response time | **~4s** (module discovery + call-graph build + BFS) |
| Module discovery | 34 modules via `settings.gradle.kts`, incl. nested modules and type-safe accessor dependencies |
| Source sets | `main`, `debug`, `prod`, `benchmark`, `testDemo`, … discovered per module (`androidTest*` excluded by design) |
Verified patterns include repositories behind project interfaces, a library-interface
override (`androidx.datastore.Serializer`), `operator fun invoke` use cases,
`@Composable` functions, extension mappers, ViewModel property-initializer chains,
and callable references. Two representative results:
- Changing `core:common`'s `asResult()` selects **14 tests across three modules**,
including ViewModel tests reachable only through `val uiState = ...stateIn(...)`
- Changing the mapper `PopulatedNewsResource.asExternalModel()` selects **14 tests**,
including 11 repository tests reachable only through `.map(Type::mapper)` chains
### Test-class selection rate
Every unit-test class in Now in Android was measured by changing a function in the
class it tests and checking whether that test class was selected:
| Test style | Selected |
|---|---|
| Plain unit tests (construct the object, call it) | 13 / 13 valid targets |
| Robolectric / Compose screenshot tests | **12 / 12** |
| Framework-dispatched callbacks (lint `Detector`) | 0 / 2 — see below |
Robolectric turned out **not** to be a barrier: those tests call the composable
themselves (`setContent { NiaTheme { ... } }`), so the call exists in the source.
What decides coverage is not the test runner but whether the test's own code
contains the call.
### What ariadne cannot see
The rule of thumb: **if the framework calls your code instead of your test calling
it, ariadne cannot connect them.** These are limits of static analysis, not bugs —
plan your CI safety net around them:
| Pattern | Status |
|---|---|
| Framework-invoked callbacks — Fragment/Activity lifecycle (`launchFragmentInContainer`), lint `Detector` methods, `Application.onCreate` | Not traced: no call written in the test |
| Reflection / DI-container wiring | Not traced |
| UDF dispatch (Flux/MVI) | `dispatch → collect` is never an edge, but wiring in `init` (or a `start()` the test calls) is covered conservatively via constructor chains. Subscriptions started by DI/lifecycle are **not** covered ([sazanami#38](https://github.com/MikhailHal/sazanami/issues/38)) |
| `stateIn` / `shareIn` chains (`map`, `onEach`, `flatMapLatest`, `combine`) | Covered — verified with exact selection |
| Instrumented tests (`androidTest*`) | Out of scope by design |
| Build scripts, resources, unscanned source sets | Not analyzed — reported explicitly in the tool response |
| Same-name top-level extensions in one package | Over-selected (receiver types are not part of top-level FQNs) — safe direction |
| KMP source sets (`commonMain`, `expect`/`actual`) | Enumerated, but resolution quality unverified ([#1](https://github.com/MikhailHal/ariadne/issues/1)) |
Full audit notes: [sazanami#29](https://github.com/MikhailHal/sazanami/issues/29),
[sazanami#38](https://github.com/MikhailHal/sazanami/issues/38).
## Requirements
- **JDK 21** or later
- **Git** — For diff detection
## Limitations
- Module discovery is convention-based: What people ask about ariadne
What is MikhailHal/ariadne?
+
MikhailHal/ariadne is mcp servers for the Claude AI ecosystem. ariadne is an MCP (Model Context Protocol) server that provides AI agents with the ability to identify affected tests. It has 1 GitHub stars and was last updated today.
How do I install ariadne?
+
You can install ariadne by cloning the repository (https://github.com/MikhailHal/ariadne) or following the README instructions on GitHub. ClaudeWave also provides quick install blocks on this page.
Is MikhailHal/ariadne safe to use?
+
MikhailHal/ariadne has not been audited yet by our security agent. Review the original repository on GitHub before using it in production.
Who maintains MikhailHal/ariadne?
+
MikhailHal/ariadne is maintained by MikhailHal. The last recorded GitHub activity is from today, with 1 open issues.
Are there alternatives to ariadne?
+
Yes. On ClaudeWave you can browse similar mcp servers at /categories/mcp, sorted by popularity or recent activity.
Deploy ariadne to your cloud
Ship this repo to production in minutes. Each platform spins up its own environment with editable env vars.
Maintain this repo? Add a badge to your README
Drop the badge into your GitHub README to show it's tracked on ClaudeWave. Each badge links back to this page and reflects the live Trust Score.
[](https://claudewave.com/repo/mikhailhal-ariadne)<a href="https://claudewave.com/repo/mikhailhal-ariadne"><img src="https://claudewave.com/api/badge/mikhailhal-ariadne" alt="Featured on ClaudeWave: MikhailHal/ariadne" width="320" height="64" /></a>More MCP Servers
Fair-code workflow automation platform with native AI capabilities. Combine visual building with custom code, self-host or cloud, 400+ integrations.
User-friendly AI Interface (Supports Ollama, OpenAI API, ...)
An open-source AI agent that brings the power of Gemini directly into your terminal.
The fastest path to AI-powered full stack observability, even for lean teams.
Real-time global intelligence dashboard. AI-powered news aggregation, geopolitical monitoring, and infrastructure tracking in a unified situational awareness interface
🕷️ An adaptive Web Scraping framework that handles everything from a single request to a full-scale crawl!