for Node services
Which function, which query, which deploy.
One agent, installed with one command, measures every exported async function and every query your app makes — and knows which function issued which query. Every release is compared against the one before it, so the finding names your deploy rather than the hour.
What it measures
- Every exported async function
- How long it took, and how much of that was its own code rather than something it called. "Did my code get slower, or did the thing I called get slower" is one number here rather than an afternoon.
- Every query, on five clients
- pg, mysql2, postgres.js, the MongoDB driver and Prisma — found and instrumented automatically, including through Mongoose and through a connection pool under load. Rows returned, columns, response size, and for MongoDB the number of network round trips a single read actually took.
- Which function issued which query
- The part that makes the rest worth reading. A query is credited to the function above it, so an N+1 shows up on the function that has the loop in it, not spread across a query that always looked fine.
- What the database did to answer
- Postgres is asked for the plan of each statement — without ever binding a parameter, so no value of yours is involved. MySQL is read from performance_schema instead: rows examined per row returned, and whether an index was used at all. Both arrive as the same finding: this reads that table without an index.
- Your HTTP routes, in and out
- Requests served, and time spent waiting on somebody else's API — so a slow third party stops being reported as your code being slow.
What it finds
Every one of these is compared against the release before it, so the finding names the change rather than the day.
An N+1 that was not there last release
A function that made one database call per request now makes fourteen. Deploy-anchored, so it names the release that introduced it.
A query that started reading the whole table
Rows per call went from 30 to 30,000. On a warm database with staging-sized data this barely moves the clock — and then it takes production down a week later.
A query that stopped using its index
The one finding here that names a cause instead of a symptom. It often arrives before anything is slow at all: the table is still small enough that a scan is fine, and it becomes an outage weeks later when it is not.
Your own code getting slower
Time spent in the function itself rather than in what it called — separated, so you are looking in the right file on the first try.
A read that turned into hundreds of network waits
MongoDB answers a cursor in batches. When a result outgrows a batch, one read becomes dozens or hundreds of sequential round trips — the documents are identical, so nothing else in the system moves except the clock.
Something that is now called in a loop
Unchanged per call — same speed, same data — but called hundreds of times where it used to be called once.
A query that quietly returns nothing
Succeeded, no error, no slow call, and came back empty where it used to come back with rows. The most common way one of these services breaks makes the numbers smaller, not bigger.
What lands in your feed
Not a dashboard to read. One card per finding, with the numbers, the release that changed them, and the evidence your coding agent needs to write the fix.
These are the numbers `pnpm e2e` prints, on anybody's clone of this repository. It seeds a service, ships a release with two real regressions, and detects them — so nothing here is a number we chose.
orders.getUserOrdersA WHERE clause went missing in a refactor, so the function pulls the whole table and filters it in JavaScript. On a warm database this barely moved the clock.
since release v2, against v1
orders.enrichOrdersA query moved inside a loop. Every one of them is fast, so no single request looks wrong — the count is the only thing that changed.
since release v2, against v1
How it installs
Hand it to the coding agent you already have open. It reads the repository, works out whether that means a Next config, a Vite plugin or a start command, and wires it in — then fixes what gets found, in the same conversation.
claude mcp add sixty \
-e SIXTY_API_KEY=sixty_sk_… \
-e SIXTY_ENDPOINT=https://ingest.sixty.sh \
-- npx -y @sixty-sh/mcp
# or by hand, in your server entry:
import { init } from '@sixty-sh/node'
init()The release identifier is picked up on its own from Vercel, Render, Railway, Fly, Heroku or GitHub Actions. The first comparison arrives after your next deploy, because there is nothing to compare one release against.
What it does not do
- MySQL and MongoDB report no plan tree. MySQL's EXPLAIN needs real parameter values and MongoDB's explain needs a real filter, and this agent drops values the moment it sees them — so for MySQL the conclusion is read from performance_schema instead, and for MongoDB there is nothing yet.
- Async generators are skipped by the build transform. Wrapping one would measure how long until the caller got an iterator, which is not the number anyone wants.
- The agent measures what it can reach from JavaScript. Time spent inside a native module, or in another process, is time it can only see as waiting.
If one of these is why you are here
Find out what your last change did.
Try it!Building something else? See Lovable apps or React Native apps — those pages are different, and so is what we can see.