Load Testing an E-Commerce Site with Claude + JMeter + MCP (No JMeter Skills Required)
Here's the setup: a QA team has four functional test cases, staging access, and zero load-testing experience. Marketing wants to know if the site survives 1,200 concurrent shoppers on Black Friday. No performance engineer on the team, no JMeter skills, no load generators.
We ran this as a real experiment (not a demo) against a live PrestaShop store, using Claude connected to jmx-mcp, an MCP server that gives an AI assistant real JMeter verbs: record a session, build a plan, upload it, start a run, read the results back. Here's exactly what that looked like, including the parts that didn't work on the first try.
Everything below is checkable: the JMX files, test data, Datadog exports, and even the full assistant transcript are linked at the bottom.
The setup
$ claude mcp add --transport http jmx-mcp http://localhost:8090/mcp
$ claude "We need to know whether our store at https://bench.pflb.us
survives Black Friday: marketing expects 1,200 shoppers on the site
at once. Here is the file with our test cases (test-cases.csv) and
our test data files (test-data/). Plan and build whatever load tests
answer that question — and be gentle with the site along the way."
That's the entire human input for the planning phase. No thread groups, no ramp-up schedule, no plan structure — just the goal and the constraint ("be gentle"). The assistant proposed three JMeter plans on its own: a 20-minute baseline at 100 users, a 95-minute staircase (600 → 1,200 → 2,200 → 600) to actually test the requirement, and a 10-minute rehearsal to cheaply verify the schedule before spending real generator hours.
From functional test cases to a load test
The input was four Jira test cases, exported as CSV — browse the catalogue, search, guest checkout, registered checkout. Walking those against the live store in a browser produced 47 requests total, because one "page" is never one request (menus, product tiles, the mini-cart all fire separately).
The part that actually matters here is correlation. Modern sites stamp every session and form with one-time security tokens; replay a recorded token and the server rejects you. A working plan has to extract a fresh token on every pass and feed it into the next request. The assistant handled this, including a PrestaShop-specific gotcha: the checkout token regenerates after the personal-information step, so the extractor has to sit after that step, not before it. Get that wrong and every checkout flow in your load test fails for a reason that has nothing to do with your actual bottleneck.
Test data came from three CSVs the team already had lying around: 20 accounts, 10 search terms, 16 product URLs. Twenty accounts, not one — because a thousand virtual shoppers hitting one account would just be testing cache, not the store.
Validating before spending generator hours
Before any real load, the plans ran through a short ladder against the live store, each rung slightly heavier:
| Rung | Hold | Samples | Failed | What it confirmed |
|---|---|---|---|---|
| 1 user/flow | 60s | 114 | 0 | Every flow works end to end, tokens are fresh |
| 3 users/flow | 90s | 413 | 0 | Flows survive running in parallel, no session collisions |
| 8 users/flow | 120s | 1,362 | 0 | Data files rotate correctly, safe to scale |
1,889 requests, zero failures, and nobody on the team opened a JMX file.
Running it for real
$ claude "The staircase plan is validated. Upload it to the PFLB
platform with its data files and start the run."
● assistant: Uploaded — the platform created the test and returned its
page: platform.pflb.us/account/tests/… Starting the run now.
● run accepted: 2,200 virtual users at peak, AWS cloud generator
● 13:32 UTC — load begins
● 15:07 UTC — platform stops the run right on schedule: 95 min sharp
95 minutes, four acts, on cloud generators (a laptop obviously can't fake 2,200 concurrent humans):

The run page, live. The whole story is visible in one screen: users climb the staircase, and once the 2,200 plateau hits, the red error band and the response-time climb follow right behind it.
| Steady state, 20 min | 600 users | 1,200 users | 2,200 users | 600 again |
|---|---|---|---|---|
| Errors | 0% | 0.11% | 32.6% | 0.04% |
| Home page p95 | 0.84s | 1.50s | 13.8s | 0.94s |
| Place order (guest) p95 | 1.25s | 3.70s | 23.0s | 1.22s |
| Place order (account) p95 | 1.63s | 4.95s | 35.2s | 2.33s |
| Host CPU | ~47% | ~83% | 99.8% | ~45% |
The interesting row here isn't the 2,200-user collapse — that's expected once you push past capacity. It's the 1,200-user column, which is the actual forecast load. Errors at 1,200 users were 0.11%. Any uptime monitor, any smoke test, any manual click-through would have reported green. Meanwhile checkout — the one flow that actually makes money — was already past its 3.5-second target. That's a quiet failure: it wouldn't show up as an incident on the day, it'd show up three weeks later as a chart of abandoned carts.
Worth flagging for anyone benchmarking off averages: mean response time at 1,200 users was a perfectly reasonable 0.33s while p95 had already tripled. Averages actively hide this class of failure — track the slow tail, not the mean.
At 2,200 users (an 80%-over-forecast stress case), host load average hit 158 on an 8-core box, where healthy is roughly 8. At that point the site's own monitoring agent couldn't complete its database queries — the watcher was queueing behind the watched.
Recovery was the one requirement the store passed cleanly: errors stopped within ~10 seconds of load dropping, response times were fully back to baseline within ~3 minutes.
Root-causing it with the AI report
Ten Datadog metrics (host CPU, load average, memory, per-container CPU for PHP and MySQL, MySQL internals) were exported as plain timestamp/value CSVs and fed into the platform's report generator. Report generation took about 3.5 minutes and produced:

The report's summary page: last stable level, failure mode, and diagnosis, generated in about 3.5 minutes from the run plus ten imported metric series.
- Host CPU at 99.9%, the PHP container pinned at 450% (4.5 of 8 cores)
- MySQL at 280% CPU but never queued — running threads peaked at 11, row-lock waits stayed at zero throughout
- Place-order response time correlated with load average at 0.95 — near-lockstep. The report's own conclusion: host queue depth is the primary driver of payment latency, not the database

Checkout response time vs. host CPU across the run. The 0.95 correlation with load average is the strongest single piece of evidence in the report — checkout slows almost exactly when the CPU queue grows.
That last point matters for anyone who assumes checkout slowness = database problem by default. Here the DB was innocent; the bottleneck was CPU capacity on the app tier, and every page was being computed from scratch under load.
The report also got two things wrong on the first pass: it misread the planned final ramp-down as an outage, and it floated a database row-locking hypothesis despite the imported lock-wait series being flat zero. Both were caught and corrected in a few minutes of review against the raw data before anyone downstream saw the report. Worth internalizing if you're using an LLM to summarize a load test run: treat the output like a junior analyst's first draft, not ground truth. It was right about 90% of the time here and wrong in the two places you'd least want to be wrong without checking.
Verdict
| Requirement | Measured at 1,200 users | Verdict |
|---|---|---|
| 1,200 users held ≥ 20 min | Held, full plateau | Pass |
| Pages p95 ≤ 1.5s | 1.50s | On the line |
| Checkout steps p95 ≤ 2s | 1.65s | Pass |
| Place order p95 ≤ 3.5s | 3.70s guest / 4.95s account | Fail |
| Errors ≤ 1% | 0.11% | Pass |
| Host CPU ≤ 85% | 83% | On the line |
| Recovery ≤ 10 min | ~3 min | Pass |
Not ready for Black Friday. Fix list, in order of leverage: more CPU for the PHP tier (more cores or a second app container), cache the pages being recomputed identically per visitor (the correlation data points at computation, not data, as the bottleneck), and a smaller MySQL buffer-pool tuning pass.
What AI actually did here vs. what it didn't
Worth being precise about this, since "AI ran my load test" invites some skepticism:
- What it replaced: the mechanical, skill-gated parts — recording flows, writing correlated JMeter scripts, building a workload model from analytics, reading hundreds of thousands of data points and correlating them against ten metric series in 3.5 minutes.
- What it didn't replace: deciding what "ready" means in numbers, checking the report's conclusions against raw data before shipping them, and owning the verdict. The two wrong claims in the AI report are the concrete argument for keeping a human in that loop — a team without someone able to catch a flat-zero lock-wait series being misread as row contention would have shipped a wrong root cause.
Total time from first prompt to a reviewed report in the dev team's hands: one working day. No performance engineer, no in-house load-testing infrastructure.
Try it
The stack is Claude + jmx-mcp (free to run locally via docker compose up, source-available) + any JMeter-compatible load generator. Everything from this run — the JMX plans, test data, Datadog exports, and the full assistant transcript — is downloadable from the original writeup, and the AI report itself is public if you want to see exactly what did and didn't survive review.
If you've got staging access and a handful of functional test cases sitting in a tracker, that's the whole prerequisite list.
Top comments (0)