---
name: jmeter-hitl-editing
description: >-
  AI-assisted Human-in-the-Loop JMeter script analysis, component addition, editing,
  and headless smoke testing. Use when the user wants to analyze, edit, add components
  to, import, or modify a JMeter JMX script, or run a local JMeter smoke test.
---

# JMeter HITL (Human-in-the-Loop) Editing

## When to Use This Skill

- User wants to analyze the structure of a JMeter JMX script
- User wants to add components (extractors, assertions, timers, etc.) to a JMX script
- User wants to edit existing components (rename, set properties, replace values)
- User wants to import an external JMX script into the artifacts folder
- User wants to run a headless JMeter smoke test to validate a script

---

## Reference

This section provides context for humans and capable models. For the step-by-step
execution instructions, skip to the **Execution** section below.

### How HITL Editing Works

The HITL tools work on **any valid JMX script**, regardless of how it was created:
- JMeter GUI, BlazeMeter export, third-party tool, CI/CD pipeline
- Generated by mcp-perf-suite (Playwright, HAR, or Swagger workflows)

The workflow is **interactive and user-directed**, not a fixed linear pipeline. The user
tells the agent what to analyze, add, or edit. The agent executes using the HITL MCP tools.

### Script Sources

A JMX script can arrive in the artifacts folder from any of these sources:

- **External / Pre-existing JMX** — User provides a path to an existing JMX file.
  Copy it into `artifacts/{test_run_id}/jmeter/` with an `imported_` prefix.
- **Playwright browser automation** — Follow the skill at `.cursor/skills/playwright-browser-automation/SKILL.md`.
- **HAR file conversion** — Follow the skill at `.cursor/skills/jmeter-har-conversion/SKILL.md`.
- **Swagger/OpenAPI conversion** — Follow the skill at `.cursor/skills/jmeter-swagger-conversion/SKILL.md`.

### Tool Reference

| Tool | Purpose | Key Parameters |
|------|---------|---------------|
| `analyze_jmeter_script` | Get script structure and node IDs. Exports versioned structure files to disk. | `test_run_id`, `jmx_filename`, `detail_level`, `export_structure`, `output_format` |
| `list_jmeter_component_types` | Discover available component types | `category` (optional filter) |
| `add_jmeter_component` | Add a new component to the script | `test_run_id`, `component_type`, `parent_node_id`, `component_config`, `position`, `dry_run` |
| `edit_jmeter_component` | Edit an existing component | `test_run_id`, `target_node_id`, `operations`, `dry_run` |
| `list_jmeter_scripts` | List JMX scripts for a test run | `test_run_id` |
| `start_jmeter_test` | Run a headless JMeter test | `test_run_id`, `jmx_path` |
| `stop_jmeter_test` | Stop a JMeter test | `test_run_id` | 
| `get_jmeter_run_status` | Poll running test for metrics | `test_run_id`, `pid` |
| `generate_aggregate_report` | Generate aggregate CSV report | `test_run_id` |
| `analyze_jmeter_log` | Analyze a JMeter log for test execution errors | `test_run_id`, `log_source` (e.g. "jmeter"/"blazemeter") |

### Edit Operations

The `edit_jmeter_component` tool supports these operations:

- `{"op": "rename", "value": "New Name"}` — Rename a component
- `{"op": "set_prop", "name": "property_name", "value": "new_value"}` — Set a property
- `{"op": "replace_in_body", "old": "find_this", "new": "replace_with"}` — Replace text in body
- `{"op": "toggle_enabled"}` — Enable or disable a component

### Common HITL Patterns

**Add assertions to all samplers:**
1. Analyze script to get all HTTPSamplerProxy node_ids
2. For each sampler, add a `response_assertion` checking for status 200

**Parameterize hardcoded values:**
1. Analyze with `detail_level="full"` to see element properties
2. Check `variables.undefined` for missing variable definitions
3. Use `edit_jmeter_component` with `replace_in_body` to swap hardcoded values
4. Add a `csv_dataset` or `user_defined_variables` component to define new variables

**Add think time between steps:**
1. Analyze to get controller node_ids
2. Add `constant_timer` or `flow_control_action` as last child of each controller

### Related Rules

- **`prerequisites.mdc`** — `test_run_id` and artifact structure validation
- **`skill-execution-rules.mdc`** — Follow steps in order, collect inputs first
- **`mcp-error-handling.mdc`** — MCP tool error handling (retry policy, reporting)
- **`jmeter-script-guardrails.mdc`** — Smoke test = 1/1/1, dry run first, analyze before edit

### Execution Output Files

After headless execution, these files are written to `artifacts/{test_run_id}/jmeter/`:

| File | Purpose |
|------|---------|
| `test-results.csv` | Raw sample data (JTL) |
| `{test_run_id}_aggregate_report.csv` | Per-label aggregate stats |
| `results_tree.csv` | Detailed sample data (JMeter built-in) |
| `aggregate_report.csv` | Aggregate data (JMeter built-in) |
| `{test_run_id}.log` | JMeter execution log |

---

## Execution

This is an **interactive workflow**. The user directs what to analyze, add, or edit.
Follow the phases below, but Steps 3-4 repeat as many times as the user needs.

---

### Collect Inputs

Ask the user for the following values. Do not proceed until all required values are collected.

```
REQUIRED:
  test_run_id = [ask user]

CONDITIONAL:
  jmx_source  = [one of: "external", "already exists", "generate from HAR/Swagger/Playwright"]
  jmx_path    = [required if jmx_source is "external" — full path to the JMX file]
```

---

### Step 1 — Import External JMX (if needed)

**When:** Only if the user is importing an external JMX file not already in the artifacts folder.

**Input:** `test_run_id`, `jmx_path`

**Action:**
1. Create directory `artifacts/{test_run_id}/jmeter/` if it does not exist
2. Copy (do NOT move) the JMX file to `artifacts/{test_run_id}/jmeter/imported_{filename}.jmx`

**Save:** `jmx_filename` = `imported_{filename}.jmx`

**Skip this step** if the JMX already exists in the artifacts folder.

---

### Step 2 — Analyze the Script

**Input:** `test_run_id`, `jmx_filename` (optional — auto-discovers if not provided)

**Action:** Call MCP tool `analyze_jmeter_script`

```
analyze_jmeter_script(
  test_run_id  = {test_run_id},
  jmx_filename = {jmx_filename},
  detail_level = "detailed"
)
```

**Expected MCP response (summary only):**

```json
{
  "status": "OK",
  "test_run_id": "{test_run_id}",
  "jmx_path": "artifacts/{test_run_id}/jmeter/...",
  "jmx_filename": "...",
  "summary": { "total_elements": 142, "by_type": { "..." } },
  "outline": "- [ThreadGroup] TC01_Thread_Group  (id: a1b2c3d4e5)\n  ...",
  "exported_files": {
    "json": "artifacts/{test_run_id}/jmeter/analysis/jmx_structure_<timestamp>.json",
    "markdown": "artifacts/{test_run_id}/jmeter/analysis/jmx_structure_<timestamp>.md"
  }
}
```

**Important:** The MCP response does NOT include the full `hierarchy`, `node_index`,
or `variables`. These are in the exported JSON file on disk.

**Save from the MCP response:** `exported_files`, `outline`, `summary`.

**Read the exported JSON file** at `exported_files.json` to obtain:
- `node_index` — all components with their `node_id` values (needed for add/edit)
- `hierarchy` — the nested script structure
- `variables` — defined, used, and undefined variables

**On error:** If `status` is `"ERROR"`, stop. Report the error to the user.

**Present** the outline and key findings to the user. Ask what they want to add or edit.

---

### Step 3 — Add or Edit Components (user-directed, repeatable)

**This step repeats** as many times as the user needs. Each cycle follows the same pattern.

#### To Add a Component:

**Input:** `test_run_id`, `parent_node_id` (from Step 2), user's request

**Action:** First, preview with dry run:

```
add_jmeter_component(
  test_run_id    = {test_run_id},
  component_type = {type from user request or list_jmeter_component_types},
  parent_node_id = {parent_node_id},
  component_config = {config based on user request},
  position       = "last",
  dry_run        = true
)
```

**Review** the dry run output with the user. If approved, apply:

```
add_jmeter_component(
  ...same parameters...,
  dry_run = false
)
```

#### To Edit a Component:

**Input:** `test_run_id`, `target_node_id` (from Step 2), user's request

**Action:** First, preview with dry run:

```
edit_jmeter_component(
  test_run_id    = {test_run_id},
  target_node_id = {target_node_id},
  operations     = [ {"op": "...", ...} ],
  dry_run        = true
)
```

**Review** the dry run output with the user. If approved, apply:

```
edit_jmeter_component(
  ...same parameters...,
  dry_run = false
)
```

**After every applied change:** Go to Step 4 to re-analyze.

---

### Step 4 — Re-Analyze After Edits

**Input:** `test_run_id`

**Action:** Call MCP tool `analyze_jmeter_script` to refresh the persisted structure
files with the latest node_ids after edits.

```
analyze_jmeter_script(
  test_run_id  = {test_run_id},
  detail_level = "detailed"
)
```

**Verify** the script structure is correct after the edit using the `outline` and
`summary` from the response.

**Save:** `exported_files` (refreshed structure files). Read the new exported JSON
file at `exported_files.json` to get the updated `node_index` — node IDs may have
changed after edits.

**Ask the user:** "Do you want to make more changes?" — If yes, go back to Step 3.

---

### Step 5 — Headless Smoke Test (optional)

**When:** Only if the user wants to run the script locally to validate it.

**Critical:** Smoke tests MUST use **1 thread, 1 second ramp-up, 1 loop**. If the
Thread Group has higher values, override to 1/1/1 before running.

#### 5a. List Scripts

```
list_jmeter_scripts(
  test_run_id = {test_run_id}
)
```

**Save:** `jmx_path` from the scripts list.

#### 5b. Start Test

```
start_jmeter_test(
  test_run_id = {test_run_id},
  jmx_path    = {jmx_path}
)
```

**Save:** `pid` from the response.

#### 5c. Monitor Until Complete

Poll `get_jmeter_run_status` every few seconds until the test finishes:

```
get_jmeter_run_status(
  test_run_id = {test_run_id},
  pid         = {pid}
)
```

#### 5d. Generate Report

```
generate_aggregate_report(
  test_run_id = {test_run_id}
)
```

**Present** the results to the user. If errors are found and the user wants to debug,
follow the debugging skill at `.cursor/skills/jmeter-debugging/SKILL.md`.

---

### Step 6 — Downstream Analysis (optional)

**When:** Only if the user wants to analyze test results with PerfAnalysis.

```
analyze_test_results(
  test_run_id = {test_run_id}
)
```

The PerfAnalysis MCP will find `test-results.csv` in the `jmeter/` folder.

---

## Error Handling

These rules apply to every step:

- Always analyze the script before any add/edit operation.
- Always use `dry_run=true` first, then `dry_run=false` to apply.
- If any MCP tool returns an error status, stop and report to the user.
- Do NOT write code to fix MCP tool issues.
- Advise the user to close JMeter GUI during HITL edits to prevent file conflicts.
