# Gaffer [Health: Active]

**Category:** 💻 Developer Tools  
**Repository:** https://github.com/gaffer-sh/mcp  
**GitHub Stars:** 1  
**Views:** 0  
**Installs:** 0  
**Upvotes:** 0  
**Directory Page:** https://allmcps.com/mcp/gaffer

## Description
Test analytics for AI agents: test history, flaky tests, failure clusters, coverage.

## Claude Desktop Quick Installation
Install path detected from listing signals. Uses `npx` (confidence: high):

```json
"mcpServers": {
  "gaffer": {
    "command": "npx",
    "args": ["-y","@gaffer-sh/mcp"]
  }
}
```

## Documentation & README

# @gaffer-sh/mcp

MCP (Model Context Protocol) server for [Gaffer](https://gaffer.sh) - give your AI assistant memory of your tests.

## What is this?

This MCP server connects AI coding assistants like Claude Code and Cursor to your Gaffer test history and coverage data. It runs in code mode: three MCP tools over a namespace of 17 functions — 16 read-only analytics functions plus `upload_test_results`. It allows AI to:

- Check your project's test health (pass rate, flaky tests, trends)
- Look up the history of specific tests to understand stability
- Get context about test failures when debugging
- Analyze code coverage and identify untested areas
- Browse all your projects (with user API Keys)
- Access test report files (HTML reports, coverage, etc.)

## Prerequisites

1. A [Gaffer](https://gaffer.sh) account with test results uploaded
2. An API Key from Account Settings > API Keys

## Setup

### Claude Code (CLI)

The easiest way to add the Gaffer MCP server is via the Claude Code CLI:

```bash
claude mcp add gaffer -e GAFFER_API_KEY=gaf_your_api_key_here -- npx -y @gaffer-sh/mcp
```

### Claude Code (Manual)

Alternatively, add to your Claude Code settings (`~/.claude.json` or project `.claude/settings.json`):

```json
{
  "mcpServers": {
    "gaffer": {
      "command": "npx",
      "args": ["-y", "@gaffer-sh/mcp"],
      "env": {
        "GAFFER_API_KEY": "gaf_your_api_key_here"
      }
    }
  }
}
```

### Cursor

Add to `.cursor/mcp.json` in your project:

```json
{
  "mcpServers": {
    "gaffer": {
      "command": "npx",
      "args": ["-y", "@gaffer-sh/mcp"],
      "env": {
        "GAFFER_API_KEY": "gaf_your_api_key_here"
      }
    }
  }
}
```

## How this server works

This server uses **code mode**. Instead of exposing one MCP tool per API call, it exposes three tools plus a `codemode` namespace you call from JavaScript. Fewer tool definitions occupy the context window, and a single execution can chain several calls.

| MCP tool | What it does |
|------|-------------|
| `execute_code` | Run JavaScript against `codemode.<function>()`. Max 20 API calls, 30s timeout. |
| `search_tools` | Find available functions by keyword. An empty query lists all of them. |
| `list_projects` | List projects. Registered only when the token is a user API Key (`gaf_`). |

```javascript
const health = await codemode.get_project_health({ projectId: "proj_abc" });
if (health.flakyTestCount > 0) {
  const flaky = await codemode.get_flaky_tests({ projectId: "proj_abc" });
  return { health, flaky };
}
return { health };
```

## Functions available via `execute_code`

| Function | Category | Description |
|------|------|-------------|
| `get_project_health` | health | Health score, pass rate, flaky count, trend |
| `get_test_history` | testing | Pass/fail history for a specific test |
| `get_flaky_tests` | testing | Tests with high flip rates (pass↔fail) |
| `list_test_runs` | testing | Recent test runs, filterable by commit/branch/status |
| `get_test_run_details` | testing | Parsed individual results for one run |
| `get_failure_clusters` | testing | Failed tests grouped by root cause |
| `get_slowest_tests` | testing | Slowest tests by P95 duration |
| `compare_test_metrics` | testing | Compare test performance between commits or runs |
| `search_failures` | testing | Search failures by error or test-name pattern, or list all recent failures |
| `get_coverage_summary` | coverage | Overall coverage metrics and trend |
| `get_coverage_for_file` | coverage | Coverage for specific files or paths |
| `get_untested_files` | coverage | Files below a coverage threshold |
| `find_uncovered_failure_areas` | coverage | Files with low coverage AND test failures |
| `get_report` | reports | Report file URLs for a test run |
| `get_report_browser_url` | reports | Signed browser-navigable report URL (30 min) |
| `get_upload_status` | uploads | Whether CI results are uploaded and processed |
| `upload_test_results` | uploads | Upload test results (**write**) — rate-limited and audit-logged |

Every function except `upload_test_results` is read-only.

## Function Reference

### `list_projects`

List all projects you have access to.

- **Input:** `organizationId` (optional), `limit` (optional, default: 50)
- **Returns:** List of projects with IDs, names, and organization info
- **Example:** "What projects do I have in Gaffer?"

### `get_project_health`

Get the health metrics for a project.

- **Input:** `projectId` (required), `days` (optional, default: 30)
- **Returns:** Health score (0-100), pass rate, test run count, flaky test count, trend
- **Example:** "What's the health of my test suite?"

### `get_test_history`

Get the pass/fail history for a specific test.

- **Input:** `projectId` (required), `testName` or `filePath` (one required), `limit` (optional)
- **Returns:** History of runs with status, duration, branch, commit, errors
- **Example:** "Is the login test flaky? Check its history"

### `get_flaky_tests`

Get the list of flaky tests in a project.

- **Input:** `projectId` (required), `threshold` (optional, default: 0.1), `days` (optional), `limit` (optional)
- **Returns:** List of flaky tests with flip rates, transition counts, run counts
- **Example:** "Which tests are flaky in my project?"

### `list_test_runs`

List recent test runs with optional filtering.

- **Input:** `projectId` (required), `commitSha` (optional), `branch` (optional), `status` (optional), `limit` (optional)
- **Returns:** List of test runs with pass/fail/skip counts, commit and branch info
- **Example:** "What tests failed in the last commit?"

### `get_test_run_details`

Get parsed test results for a specific test run.

- **Input:** `testRunId` (required), `projectId` (required), `status` (optional filter), `limit` (optional)
- **Returns:** Individual test results with name, status, duration, file path, errors
- **Example:** "Show me all failed tests from this test run"

### `get_report`

Get URLs for report files uploaded with a test run.

- **Input:** `testRunId` (required)
- **Returns:** List of files with filename, size, content type, download URL
- **Example:** "Get the Playwright report for the latest test run"

### `get_report_browser_url`

Get a browser-navigable URL for viewing a test report.

- **Input:** `projectId` (required), `testRunId` (required), `filename` (optional)
- **Returns:** Signed URL valid for 30 minutes
- **Example:** "Give me a link to view the test report"

### `get_slowest_tests`

Get the slowest tests in a project, sorted by P95 duration.

- **Input:** `projectId` (required), `days` (optional), `limit` (optional), `framework` (optional), `branch` (optional)
- **Returns:** List of tests with average and P95 duration, run count
- **Example:** "Which tests are slowing down my CI pipeline?"

### `compare_test_metrics`

Compare test metrics between two commits or test runs.

- **Input:** `projectId` (required), `testName` (required), `beforeCommit`/`afterCommit` OR `beforeRunId`/`afterRunId`
- **Returns:** Before/after metrics with duration change and percentage
- **Example:** "Did my fix make this test faster?"

### `get_coverage_summary`

Get the coverage metrics summary for a project.

- **Input:** `projectId` (required), `days` (optional, default: 30)
- **Returns:** Line/branch/function coverage percentages, trend, report count, lowest coverage files
- **Example:** "What's our test coverage?"

### `get_coverage_for_file`

Get coverage metrics for specific files or paths.

- **Input:** `projectId` (required), `filePath` (required - exact or partial match)
- **Returns:** List of matching files with line/branch/function coverage
- **Example:** "What's the coverage for our API routes?"

### `get_untested_files`

Get files with little or no test coverage.

- **Input:** `projectId` (required), `maxCoverage` (optional, default: 10%), `limit` (optional)
- **Returns:** List of files below threshold sorted by coverage (lowest first)
- **Example:** "Which files have no tests?"

### `find_uncovered_failure_areas`

Find code areas with both low coverage AND test failures (high risk).

- **Input:** `projectId` (required), `days` (optional), `coverageThreshold` (optional, default: 80%)
- **Returns:** Risk areas ranked by score, with file path, coverage %, failure count
- **Example:** "Where should we focus our testing efforts?"

### `get_failure_clusters`

Group failed tests by root cause using error message similarity.

- **Input:** `projectId` (required), `testRunId` (required)
- **Returns:** Clusters of failed tests grouped by similar error messages, with representative error and test count
- **Example:** "Are these 15 failures from the same bug?"

### `search_failures`

Search past failures by error message, stack trace, or test name — or list every failure in the window.

- **Input:** `query` (optional — omit to return all failures), `projectId` (required for `gaf_` keys), `searchIn` (optional: `errors`/`names`/`all`, default `all`), `days` (optional, default: 30), `branch` (optional), `limit` (optional, default: 20)
- **Returns:** Matching failures with test name, error message, run and commit context, plus `truncated` when scan caps cut the list short
- **Example:** "Have we seen this connection-refused error before?" / "What failed in the last 7 days?"

### `get_upload_status`

Check if CI results have been uploaded and processed.

- **Input:** `projectId` (required), `sessionId` (optional), `commitSha` (optional), `branch` (optional)
- **Returns:** Upload session(s) with processing status, linked test runs and coverage reports
- **Example:** "Are my test results ready for commit abc123?"

### `upload_test_results`

Upload structured test results. This is the only function that writes.

Use it when you have results in hand — parsed from CI output or a runner's JSON
report — and no Gaffer CLI is available to upload them.

- **Input:** `projectId` (required for `gaf_` keys), `framework` (required), `tests` (required), `branch`, `commitSha`, `ciProvider`, `startedAt`, `finishedAt`, `coverage`
- **Returns:** `uploadSessionId`, the generated `runId`, and the derived pass/fail/skip summary
- **Example:** "Upload these 42 parsed pytest results so we can track them"

`runId`, the run timestamps and the summary are derived from `tests` — pass
`startedAt`/`finishedAt` only if you know the real wall-clock window.

Two constraints worth knowing:

- **Not idempotent.** Each call creates a new run, so a retry after an uncertain
  failure produces a duplicate. Check `get_upload_status` instead of retrying.
- **Rate-limited per project**, and every call is written to the project's audit
  log with the id of the credential that made it.

Processing is asynchronous: results take a few seconds to become visible to the
read functions.

## Agentic CI Workflows

These workflows show how an AI agent diagnoses CI failures, waits for results, and finds coverage gaps. Each step is a `codemode` function, so a whole chain runs inside one `execute_code` call rather than one round-trip per step.

### Workflow: Diagnose CI Failures

```
list_test_runs(projectId, status="failed")
  → get_test_run_details(projectId, testRunId, status="failed")
  → get_failure_clusters(projectId, testRunId)
  → get_test_history(projectId, testName="...")
  → compare_test_metrics(projectId, testName, beforeCommit, afterCommit)
```

1. Find the failed test run
2. Get individual failure details with stack traces
3. Group failures by root cause — often 15 failures are 2-3 bugs
4. Check if each failure is new (regression) or recurring
5. Verify fixes by comparing before/after

### Workflow: Wait for Results

```
get_upload_status(projectId, commitSha="abc123")
  → poll until processingStatus="completed"
  → get_test_run_details(projectId, testRunId)
```

1. Check if results for a commit have been uploaded
2. Wait for processing to complete
3. Use linked test run IDs to get results

### Workflow: Find Coverage Gaps

```
find_uncovered_failure_areas(projectId)
  → get_untested_files(projectId)
  → get_coverage_for_file(projectId, filePath="src/critical/")
```

1. Find files with both low coverage and test failures (highest risk)
2. Find files with no coverage at all
3. Drill into specific directories for targeted analysis

### Function Quick Reference

| Agent Question | Function |
|---|---|
| "What failed?" | `get_test_run_details` |
| "Same root cause?" | `get_failure_clusters` |
| "Seen this error before?" | `search_failures` |
| "Is it flaky?" | `get_flaky_tests` |
| "Is this new?" | `get_test_history` |
| "Did my fix work?" | `compare_test_metrics` |
| "Are results ready?" | `get_upload_status` |
| "What's untested?" | `find_uncovered_failure_areas` |
| "What's slow?" | `get_slowest_tests` |

## Prioritizing Coverage Improvements

When using coverage tools to improve your test suite, combine coverage data with codebase exploration for best results:

### 1. Understand Code Utilization

Before targeting files purely by coverage percentage, explore which code is actually critical:

- **Find entry points:** Look for route definitions, event handlers, exported functions - these reveal what code actually executes in production
- **Find heavily-imported files:** Files imported by many others are high-value targets
- **Identify critical business logic:** Look for files handling auth, payments, data mutations, or core domain logic

### 2. Prioritize by Impact

Low coverage alone doesn't indicate priority. Consider:

- **High utilization + low coverage = highest priority** - Code that runs frequently but lacks tests
- **Large files with 0% coverage** - More uncovered lines means bigger impact on overall coverage
- **Files with both failures and low coverage** - Use `find_uncovered_failure_areas` for this

### 3. Use Path-Based Queries

The `get_untested_files` tool may return many frontend components. For backend or specific areas:

```
# Query specific paths with get_coverage_for_file
get_coverage_for_file(filePath="server/services")
get_coverage_for_file(filePath="src/api")
get_coverage_for_file(filePath="lib/core")
```

### 4. Iterative Improvement

1. Get baseline with `get_coverage_summary`
2. Identify targets with `get_coverage_for_file` on critical paths
3. Write tests for highest-impact files
4. Re-check coverage after CI uploads new results
5. Repeat

## Authentication

### User API Keys (Recommended)

User API Keys (`gaf_` prefix) provide read-only access to all projects across your organizations. Get your API Key from: **Account Settings > API Keys**

### Project Tokens

Project Tokens (`gfr_` prefix) are designed for uploading test results and only provide access to a single project. When you use one, omit `projectId` — it resolves automatically. User API Keys are preferred for the MCP server because they enable `list_projects` and read across projects.

## Environment Variables

| Variable | Required | Description |
|----------|----------|-------------|
| `GAFFER_API_KEY` | Yes | Your Gaffer API Key (starts with `gaf_`) |
| `GAFFER_API_URL` | No | API base URL (default: `https://app.gaffer.sh`) |

## Local Development

```bash
pnpm install
pnpm build
```

Test locally with Claude Code (use absolute path to built file):

```json
{
  "mcpServers": {
    "gaffer": {
      "command": "node",
      "args": ["/absolute/path/to/dist/index.js"],
      "env": {
        "GAFFER_API_KEY": "gaf_..."
      }
    }
  }
}
```

## License

MIT

