Back to MCP Servers

Tarn

CLI-first API testing tool with structured JSON failures (`failure_category`, `error_code`, `hints`) so AI agents can branch on the taxonomy without parsing stderr. MCP tools: `tarn_run`, `tarn_validate`, `tarn_fix_plan`, `tarn_inspect`, `tarn_rerun_failed`, and more. Tests are …

developer-toolsgotestingapiaiagent
By NazarKalytiuk
3Updated 1 week agoRustMIT

Installation

npx -y tarn

Configuration

{
  "mcpServers": {
    "tarn": {
      "command": "npx",
      "args": ["-y", "tarn"]
    }
  }
}

How to use

  1. Run the installation command above (if needed)
  2. Open your Claude Code settings file (~/.claude/settings.json)
  3. Add the configuration to the mcpServers section
  4. Restart Claude Code to apply changes
<p align="center"> <strong>Tarn</strong><br> <em>API tests an AI agent can write, run, and debug.</em> </p> <p align="center"> <a href="https://github.com/NazarKalytiuk/tarn/actions/workflows/ci.yml"><img src="https://github.com/NazarKalytiuk/tarn/actions/workflows/ci.yml/badge.svg" alt="CI"></a> <a href="https://github.com/NazarKalytiuk/tarn/blob/main/docs/CONFORMANCE.md"><img src="https://img.shields.io/github/actions/workflow/status/NazarKalytiuk/tarn/ci.yml?branch=main&label=conformance" alt="Conformance"></a> <a href="https://github.com/NazarKalytiuk/tarn/releases/latest"><img src="https://img.shields.io/github/v/release/NazarKalytiuk/tarn" alt="Release"></a> <a href="https://github.com/NazarKalytiuk/tarn/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue" alt="License"></a> </p> <p align="center"> <a href="https://nazarkalytiuk.github.io/tarn/">Docs</a> &middot; <a href="https://nazarkalytiuk.github.io/tarn/getting-started.html">Get started</a> &middot; <a href="https://nazarkalytiuk.github.io/tarn/mcp.html">MCP integration</a> &middot; <a href="./docs/AI_WORKFLOW_DEMO.md">Agent loop demo</a> </p>

Tarn is a CLI-first API testing tool written in Rust. Tests are .tarn.yaml files. Output is structured JSON with categorized failures and remediation hints, so an agent — Claude Code, Codex, opencode, Cursor, Windsurf, pi — can write a test, run it, read what broke, and fix it without scraping logs.

# tests/health.tarn.yaml
name: Health check
steps:
  - name: GET /health
    request:
      method: GET
      url: "{{ env.base_url }}/health"
    assert:
      status: 200
$ tarn run
 TARN  Running tests/health.tarn.yaml

 ● Health check
   ✓ GET /health (4ms)

 Results: 1 passed (15ms)

When something breaks, --format json returns the same run as machine-readable data with failure_category, error_code, and the offending request/response. The tarn-mcp companion exposes a tarn_fix_plan tool that turns that report into actionable suggestions an agent can apply directly.

Why Tarn?

  • Structured failures, not log scraping — every failure carries a stable category, error code, and remediation hints. Agents branch on taxonomy, not regex.
  • MCP-nativetarn-mcp exposes list, validate, run, and fix_plan as structured tools for Claude Code, Codex, opencode, Cursor, and Windsurf (and pi via the skill + CLI). See AI agent integrations.
  • YAML the model already knows — no DSL to teach, no test framework to bootstrap. An LLM writes a .tarn.yaml and ships.
  • One static binarycurl | sh install, no runtime, drops into any CI image.
  • Batteries included — REST + GraphQL, captures, cookies, multipart, includes, polling, Lua, parallel execution, 7 output formats.

Install

# macOS / Linux
curl -fsSL https://raw.githubusercontent.com/NazarKalytiuk/tarn/main/install.sh | sh

# from source
cargo install --git https://github.com/NazarKalytiuk/tarn.git --bin tarn

Pre-built binaries for macOS (Intel + Apple Silicon), Linux (amd64 + arm64), and Windows (amd64 zip) are on the releases page, each with a tarn-checksums.txt for SHA-256 verification and a generated tarn.rb Homebrew formula artifact. The installer also lays down tarn-mcp and tarn-lsp when present in the archive. Set TARN_INSTALL_DIR to install elsewhere. Container path: ghcr.io/<owner>/tarn:<tag> from the release workflow. Manual verification works with shasum -a 256 -c tarn-checksums.txt.

Quick Start

The 60-second path:

tarn init                              # scaffold tests/ + tarn.env.yaml + advanced templates
# edit tarn.env.yaml so base_url points at your API
tarn run                               # runs every .tarn.yaml under tests/

Layer on the flags you actually need:

tarn run --format json --json-mode compact   # structured output for agents and CI
tarn run --env staging                       # use a named environment
tarn run --only-failed                       # quiet down a noisy run
tarn run --watch                             # rerun on file changes
tarn run --parallel                          # run files in parallel
tarn list --tag smoke                        # what would run, without running
tarn fmt --check                             # canonical YAML, CI-gateable

Debugging a failed run

Default to the failures-first loop — it keeps agents and humans off the megabyte-scale full report until they actually need it:

tarn validate <path>                  # syntax/config before running
tarn run <path>                       # writes .tarn/runs/<run_id>/
tarn failures                         # root-cause groups; cascades collapsed
tarn inspect last FILE::TEST::STEP    # full context for ONE failure
# patch tests or application code
tarn rerun --failed                   # replay only failing (file, test) pairs
tarn diff prev last                   # confirm fixed / new / persistent

tarn failures groups by root-cause fingerprint and collapses skipped_due_to_failed_capture cascades into their upstream entry — one failing step with five downstream skips surfaces as one entry with cascades: 5, not six. tarn inspect supports run-id aliases last / latest / @latest / prev and drills into one record via FILE[::TEST[::STEP]]. tarn rerun --failed stamps rerun_source onto the new report. tarn diff prev last buckets failure fingerprints into new / fixed / persistent so you can confirm a patch without re-reading the full report.

Reach for .tarn/runs/<run_id>/report.json only when failures + inspect cannot answer the question. See plugin/skills/tarn-api-testing/SKILL.md (Failures-First Loop) and docs/TROUBLESHOOTING.md for the canonical agent-facing guidance, including a worked example of a mutation endpoint whose response shape changed from {"uuid": "..."} to {"request": {"uuid": "..."}} and the $.uuid$.request.uuid fix.

Hello World

A fully local demo with no external network dependency:

PORT=3000 cargo run -p demo-server &
cargo run -p tarn -- run examples/demo-server/hello-world.tarn.yaml

More local scenarios — redirects, cookies, forms, error responses, authenticated CRUD — live in examples/demo-server/.

Documentation

Full guides, CLI reference, AI workflow walkthroughs, and editor setup live on the docs site:

https://nazarkalytiuk.github.io/tarn/

In-repo docs to start with:

The reference sections below mirror what's on the docs site — useful when reading on GitHub directly.

AI agent integrations

Tarn drives any agent that speaks MCP or has a shell. tarn-mcp exposes list / validate / run / fix_plan (plus the failures-first tools); the tarn-api-testing skill teaches the loop. This table is the canonical supported-agents list — per-agent setup kits live under editors/.

AgentHow Tarn plugs inSetup
Claude Codetarn-mcp + skill plugin, plus the tarn-lsp pluginmarketplace · editors/claude-code/tarn-lsp-plugin
OpenAI Codextarn-mcp (codex mcp add) + .agents/skills/ skill + AGENTS.mdeditors/codex
opencodetarn-mcp + tarn-lsp + skill via opencode.jsonceditors/opencode
pitarn-api-testing skill + tarn CLI (no native MCP; optional MCP via adapter)editors/pi
Cursortarn-mcp via .cursor/mcp.jsonMCP setup
Windsurftarn-mcp via .windsurf/mcp.jsonMCP setup
Neovim / Helix / Zed / VS Codetarn-lsp language serverdocs/TARN_LSP.md

A reproducible write → run → read-failure → fix loop for any of these lives in examples/agent-loop/.

Table of Contents

Test File Format

Test files use .tarn.yaml and can be organized in any directory structure.

Minimal Test

name: Health check
steps:
  - name: GET /health
    request:
      method: GET
      url: "http://localhost:3000/health"
    assert:
      status: 200

request.method accepts standard verbs and custom tokens such as PURGE or PROPFIND.

Full Format

version: "1"
name: "User CRUD Operations"
description: "Tests complete user lifecycle"
tags: [crud, users, smoke]

env:
  base_url: "http://localhost:3000/api/v1"

defaults:
  headers:
    Content-Type: "application/json"
  timeout: 5000
  retries: 1

tests:
  create_and_verify:
    description: "Create a user, then verify it exists"
    tags: [smoke]
    steps:
      - name: Create user
        request:
          method: POST
          url: "{{ env.base_url }}/users"
          body:
            name: "Jane Doe"
            email: "jane.{{ $random_hex(6) }}@example.com"
        capture:
          user_id: "$.id"
        assert:
          status: 201
          body:
            "$.name": "Jane Doe"
            "$.id": { type: string, not_empty: true }

      - name: Verify user
        request:
          method: GET
          url: "{{ env.base_url }}/users/{{ capture.user_id }}"
        assert:
          status: 200
          body:
            "$.id": "{{ capture.user_id }}"

Setup and Teardown

setup runs once before all tests. teardown runs after all tests even if tests fail.

name: "CRUD with auth"

setup:
  - name: Login
    request:
      method: POST
      url: "{{ env.base_url }}/auth/login"
      body:
        email: "{{ env.admin_email }}"
        password: "{{ env.admin_password }}"
    capture:
      auth_token: "$.token"

teardown:
  - name: Cleanup
    request:
      meth

…
View source on GitHub