# Chaos MCP

> Chaos-MCP is an MCP stdio server that runs isolated mutation testing against a target codebase to expose gaps in its test suite. It wraps four language engines — StrykerJS (TS/JS), cosmic-ray (Python), cargo-mutants (Rust), Infection (PHP) — and exposes three tools: audit_code_resilience, triage_test_coverage, and estimate_audit.

- **Type:** MCP server
- **Install:** `agentstack add mcp-araneadev-chaos-mcp`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [AraneaDev](https://agentstack.voostack.com/s/araneadev)
- **Installs:** 0
- **Category:** [Integrations](https://agentstack.voostack.com/c/integrations)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [AraneaDev](https://github.com/AraneaDev)
- **Source:** https://github.com/AraneaDev/Chaos-MCP

## Install

```sh
agentstack add mcp-araneadev-chaos-mcp
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Chaos-MCP

> On-demand micro-mutation sandbox for AI test verification — maps holes in unit tests by running isolated mutation testing via the Model Context Protocol.

[](https://github.com/AraneaDev/Chaos-MCP/releases)
[](https://mcpobservatory.com/servers/github:AraneaDev/Chaos-MCP/security)
[](./LICENSE)
[](./.github/workflows/ci.yml)
[](#development)
[](#)

> **Pre-release / in active development.** Chaos-MCP is **not yet published to npm**. The source is public on [GitHub](https://github.com/AraneaDev/Chaos-MCP) — install from source (see [Installation](#installation)). Any `npm install -g` / `npx` commands in this README describe the planned published experience and do not work yet.

Chaos-MCP is an [MCP (Model Context Protocol)](https://modelcontextprotocol.io/) server that exposes three tools — `audit_code_resilience` (audit a single file), `triage_test_coverage` (rank a whole tree weakest-first), and `estimate_audit` (cheap pre-flight mutant count / timing estimate) — which run isolated mutation testing against your source to find weaknesses in the local test suite. It intentionally injects logical faults (like changing `>` to `>=`) and checks whether your tests catch them. Surviving mutants indicate test coverage holes.

## Features

- **4 Languages Supported** — TypeScript/JavaScript (StrykerJS), Python (cosmic-ray), Rust (cargo-mutants), PHP (Infection)
- **Sandbox Isolation** — all mutation runs execute in temporary directories; your real workspace is never touched
- **Auto-Detection** — automatically detects project type, test runner, and workspace root
- **Async Subprocesses** — all mutation-tool execution uses async `execFile`/`exec` (subprocess runs never block the event loop; the one-time sandbox copy is synchronous)
- **Rich Tool Schema** — supports line scoping, mutator denylists, concurrency control, dry-run mode, incremental runs, and output format selection
- **Pre-flight Estimation** — `estimate_audit` gives a fast mutant count (exact for Rust, approximate for others) and optional timing estimate before you commit to a full run
- **Gate Mode** — pass `minScore` to `audit_code_resilience` or `triage_test_coverage` to get a machine-readable pass/fail field for CI pipelines
- **Cross-Platform** — works on macOS, Linux, and Windows (with junction fallback for symlinks)

## Installation

While in development, the only supported install path is **from source** — clone the repo, build, and register the built entrypoint with your MCP client.

```bash
git clone https://github.com/AraneaDev/Chaos-MCP.git
cd Chaos-MCP
npm install
npm run build      # compiles to build/index.js
```

Register it with an MCP client (Claude Code example):

```bash
claude mcp add chaos-mcp -- node /absolute/path/to/ChaosMCP/build/index.js
```

> **Planned (not available yet):** once published, install will be `npm install -g chaos-mcp` or run on demand via `npx chaos-mcp`. These do not work until the package ships to npm.

### Prerequisites — language mutation tools

Chaos-MCP does **not** bundle the per-language mutation engines or install them for you; it shells out to whichever tool matches the file you audit. Install only the one(s) for the languages you intend to audit. If a tool is missing, the audit returns a clear error naming the exact install command — it never fails silently.

| Language | Engine | Install |
| --- | --- | --- |
| TypeScript / JavaScript | [StrykerJS](https://stryker-mutator.io/) | `npm install --save-dev @stryker-mutator/core` (in the target project) — note: StrykerJS 9.x's vitest-runner is not compatible with vitest 3.x's dropped `--related` API. If the target uses vitest 3.x, downgrade it to `vitest@^2.1.x` for the audit, or wait for StrykerJS 10.x. |
| Python | [cosmic-ray](https://github.com/sixty-north/cosmic-ray) | `pipx install cosmic-ray` — or `pip install cosmic-ray` inside a virtualenv |
| Rust | [cargo-mutants](https://github.com/sourcefrog/cargo-mutants) | `cargo install cargo-mutants` |
| PHP | [Infection](https://infection.github.io/) | `composer require --dev infection/infection` — also enable a coverage driver (Xdebug or PCOV) |

Notes:
- The tool itself must be on `PATH` (or, for StrykerJS, resolvable from the target project's `node_modules`), and the **language toolchain** it builds on must already be present — Node.js for StrykerJS, a Python interpreter for cosmic-ray, a Rust/Cargo toolchain for cargo-mutants, and PHP + Composer with a coverage driver (Xdebug or PCOV) for Infection.
- **Python / cosmic-ray:** on modern distros a bare `pip install cosmic-ray` is blocked by [PEP 668](https://peps.python.org/pep-0668/) ("externally-managed-environment"); use `pipx install cosmic-ray` (isolated) or install inside an activated virtualenv. Chaos-MCP generates cosmic-ray's `config.toml` for you (scoped to the target file) and runs `baseline → init → exec → dump` in the sandbox — no per-project config needed. cosmic-ray runs its **full operator set** (no per-file line-scoping), so auditing a large file is slow. Two `cosmicray` config knobs keep big audits tractable: `testSelection` scopes the per-mutant test run (e.g. `["tests/unit/test_x.py"]` or `["-m","unit"]`), and `excludeOperators` (regexes, applied via `cr-filter-operators`) bounds the **mutant count** by skipping whole operator families — e.g. `["core/NumberReplacer", "core/ReplaceBinaryOperator.*"]` drops ~half the mutants on an arithmetic-heavy file. Excluded mutants are omitted from the score (a scoped audit).
- These engines run **inside the sandbox** against a copy of your workspace; Chaos-MCP never installs or modifies anything in your real project.

## Quick Start

### 1. Start the Server

Normally your MCP client launches the server for you (see [Installation](#installation)). To run it directly from a source checkout:

```bash
# From the repo root, after `npm run build`
npm start                                  # → node build/index.js
node build/index.js --verbose              # diagnostic logging to stderr
node build/index.js --config ./chaos-mcp.config.json
```

### 2. Call the Tool from Your MCP Client

The primary tool is `audit_code_resilience` (the batch tool `triage_test_coverage` is documented [below](#batch-triage--triage_test_coverage); the lightweight pre-flight tool `estimate_audit` is documented [below](#pre-flight-estimate--estimate_audit)).

**Minimal example:**
```json
{
  "filePath": "src/utils/math.ts"
}
```

**Full example with all options:**
```json
{
  "filePath": "src/utils/math.ts",
  "timeoutMs": 120000,
  "lineScope": { "start": 10, "end": 80 },
  "mutatorDenylist": ["StringLiteral"],
  "concurrency": 4,
  "incremental": true,
  "ignorePatterns": ["fixtures/", "snapshots/"],
  "outputFormat": "text",
  "enrich": false,
  "maxSurvivors": 20,
  "severityFloor": "medium"
}
```

**Get enriched, severity-ranked guidance on survivors (on by default):**

Enrichment is enabled by default. Each surviving / no-coverage line is augmented with four fields: a `severity` rating (`high`, `medium`, or `low`) based on the mutator's semantics (e.g. boundary operators and logical operators rank high), a `why` explanation of why the gap is dangerous, a `hint` describing the kind of test that would kill it, and a `context` snippet of the surrounding source lines. Survivors are re-ranked severity-first so the most critical gaps appear first. To disable enrichment and return the plain unranked output, pass `"enrich": false`.

TypeScript targets produce the richest output because StrykerJS exposes per-mutant operator detail; Python (cosmic-ray) targets also produce severity-ranked output, mapping the tool's authoritative operator name to a canonical category; targets whose tool can't expose a per-mutant operator fall back to `severity: "unknown"` with a generic why/hint.

**Cap and filter the survivor list:**
```json
{
  "filePath": "src/utils/math.ts",
  "maxSurvivors": 5,
  "severityFloor": "high"
}
```
`maxSurvivors` caps how many survivor (and no-coverage) line groups are returned after severity ranking (default: 10; configurable via `defaultMaxSurvivors`). Hidden groups are counted in `survivorsTruncated` / `noCoverageTruncated` in the output. `severityFloor` drops groups below the given severity level (requires enrichment, which is on by default); dropped groups are counted in `survivorsFiltered` / `noCoverageFiltered`.

**Scope to just your uncommitted changes:**
```json
{
  "filePath": "src/utils/math.ts",
  "diffBase": "HEAD"
}
```
Mutation-tests only the lines you've changed since the last commit.

**Verify your new tests killed the previous survivors:**
```json
{
  "filePath": "src/utils/math.ts",
  "baseline": { "survivors": [{ "line": 42, "mutators": { "ConditionalExpression": 1 } }] }
}
```
Re-runs only the baseline lines and reports which previously-uncaught mutants are now killed:
```json
{ "mode": "verify", "baselineTotal": 1, "killedCount": 1,
  "nowKilled": [{ "line": 42, "mutator": "ConditionalExpression" }],
  "stillSurviving": [], "newSurvivors": [] }
```

### 3. Interpret the Results

The output is **bundled and deduplicated** to stay token-efficient: mutants are grouped by line (with a per-line count of each mutator type), `survivors` (tests ran but didn't catch) and `noCoverage` (no test reached the mutant) are reported separately at line+mutator granularity, and the explanatory note appears once instead of being repeated for every mutant. Because the split is per-mutator, the same line can appear in both lists (e.g. a live expression that survived next to an unreachable fallback that no test reached). Survivors and no-coverage entries also include a `changes` sample — a capped, deduped list of `original → mutated` edits — for TypeScript and Rust targets (best-effort; absent for Python, which doesn't expose per-mutant detail). When `diffBase` is used, the output may include a `scopeNote` (a top-level JSON field / a `Scope:` text line) reporting scoping decisions — e.g. a skipped run when nothing changed, or a whole-file fallback for Python/Rust targets.

**JSON output (default — emitted as a single compact line):**
```json
{
  "target": "src/utils/math.ts",
  "mutationScore": "91.67%",
  "summary": { "total": 12, "killed": 11, "survived": 1, "worstSeverity": "high" },
  "survivors": [
    {
      "line": 42, "mutators": { "ConditionalExpression": 1 }, "changes": ["a > b → a >= b"],
      "severity": "high",
      "why": "a branch condition was forced to a constant; a test passed without exercising both arms.",
      "hint": "add tests that take BOTH the true and the false branch.",
      "context": ["41: if (a > b) {", "42:   return a;", "43: }"]
    }
  ],
  "noCoverage": [],
  "suggestedTestFile": { "path": "src/utils/__tests__/math.test.ts", "exists": false },
  "note": "survivors: mutants your tests ran but did not kill. noCoverage: mutants no test reached (per line+mutator, so a line may appear here and in survivors). mutators = type→count. Add or strengthen tests targeting these. changes = sampled original→mutated edits for that line (capped)."
}
```

The tool response also carries a `structuredContent` field (in addition to the standard text content block) so MCP clients that support it can consume the data directly without parsing JSON from text. The text block is retained for compatibility with clients that read `content[0].text`.

`suggestedTestFile` is included when there are survivors or no-coverage entries (i.e. when the mutation score is below 100%), pointing to the conventional test file path for the audited source file (e.g. `src/utils/__tests__/math.test.ts` for `src/utils/math.ts`). The `exists` flag indicates whether the file already exists on disk.

**Text output** (`"outputFormat": "text"`):
```
Chaos-MCP Audit Report: src/utils/math.ts
Mutation score: 91.67% (11/12 killed, 1 survived)
Survivors (line: mutators):
  42: ConditionalExpression  (a > b → a >= b)
Add or strengthen tests targeting these lines to kill the survivors.
```

## Tool Parameters

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `filePath` | `string` | Yes | Workspace-relative path to the file (`.ts`, `.js`, `.tsx`, `.jsx`, `.py`, `.go`, `.rs`) |
| `timeoutMs` | `number` | No | Max run time in ms (default: 300000 / 5 min) |
| `lineScope` | `{ start, end }` | No | 1-based line range (StrykerJS only) |
| `diffBase` | `string` | No | Auto-scope mutation to git-changed lines. `"HEAD"` (uncommitted), `"staged"`, or a git ref (e.g. `"main"`, via merge-base). Mutually exclusive with `lineScope`. Line-level scoping is StrykerJS-only; other languages run whole-file with a note. No changes vs base → run skipped. |
| `baseline` | `object` | No | Verify mode. Pass back a prior run's `{ survivors, noCoverage }` to re-test only those mutants and get a delta (`nowKilled` / `stillSurviving` / `newSurvivors`). Re-run auto-scopes to the baseline lines (StrykerJS) or whole-file (other languages). Mutually exclusive with `diffBase`/`lineScope`. Verify mode keys on line numbers, so run it after **adding tests** — not after editing the source under test, since edits shift line numbers and would misreport which mutants were killed. |
| `mutatorAllowlist` | `string[]` | No | Not supported in StrykerJS v9 — ignored (use `mutatorDenylist`) |
| `mutatorDenylist` | `string[]` | No | Stryker mutator names to exclude |
| `concurrency` | `number` | No | Parallel mutation workers (StrykerJS only) |
| `dryRun` | `boolean` | No | Validate test suite only, no mutations (StrykerJS only) |
| `outputFormat` | `"json"` \| `"text"` | No | Output format (default: `"json"`) |
| `incremental` | `boolean` | No | Reuse previous run results (StrykerJS only) |
| `ignorePatterns` | `string[]` | No | Substring patterns to exclude from sandbox copy |
| `enrich` | `boolean` | No | Annotate each survivor with severity, why-it-matters, a test hint, and source context — and rank severity-first. **Default: `true`** (pass `false` to disable and return plain unranked output). Richest for TypeScript; Python degrades to `severity: "unknown"`. |
| `maxSurvivors` | `integer ≥ 1` | No | Cap on how many survivor (and no-coverage) line groups are returned after severity ranking. Hidden groups counted in `survivorsTruncated`/`noCoverageTruncated`. Precedence: arg > `defaultMaxSurvivors` config > 10. |
| `severityFloor` | `"high"` \| `"medium"` \| `"low"` | No | Drop survivor groups below this severity (requires enrichment, on by default). Dropped groups counted in `survivorsFiltered`/`noCoverageFiltered`. `"unknown"`-severity groups are below `"low"` and are dropped by any floor. |
| `runId` | `string` | No | Verify mode by cached id: re-run against the survivor baseline saved from a prior audit (the `runId` it returned). Mutually exclusive with `baseline`, `diffBase`, and `lineScope`. Unknown or expired ids (cache TTL: ~24 h) return an error. |
| `suppress` | `object[]` | No | Mark mutants as equivalent (unkillable). Each entry: `{ "line": N, "mutator": "MutatorName" }` (reason is an optional string explaining why the mutant is equivalent). Persisted to `.chaos-mcp/suppressions.json`; suppressed mutants are auto-excluded from the score denominator and from future `audit` and `triage` output. The output field `suppressedCount` reports how many were excluded. |
| `unsuppress` | `object[]` | No | Remove previously-suppressed mutants for this file. Each entry: `{ "line": N, "mutator": "MutatorName" }`. |
| `minScore` | `number 0–100` | No | Gate threshold. When the mutation score is below this value, the output includes `gate: { minScore, passed: false }`. Never an error. Uses the suppression-adjusted score. |

See [`CONTRIBUTING.md`](CONTRIBUTING.md) for development setup and the full parameter semantics.

## State & the verify loop

### Verify loop via `runId`

Every successful, non-verify `audit_code_resilience` call returns a `runId` (an 8-character id) in its JSON output. Use it to re-verify wi

…

## Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [AraneaDev](https://github.com/AraneaDev)
- **Source:** [AraneaDev/Chaos-MCP](https://github.com/AraneaDev/Chaos-MCP)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** yes
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/mcp-araneadev-chaos-mcp
- Seller: https://agentstack.voostack.com/s/araneadev
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
