ok hides one.
Why empty is split out
A call that succeeded and returned nothing is the failure the outcome field hides. Burying it inside the green bar is exactly how it stays hidden — sook is drawn as ok − empties and empty gets its own slice.
Both the overview and the tool page render this from the same function, so the two cannot disagree about what “ok” counts. See Empty results.
What these outcomes measure
MCPulse instruments theMcpServer layer, not the wire.
A thrown error and a deliberately returned isError are distinguishable to MCPulse because the SDK wraps your tool callbacks — but they arrive at the model identically, once McpServer has normalised them.
That is measured rather than assumed. A fixture server that throws, returns isError, and fails validation on purpose, probed through a real MCP client:
All three failures are one thing on the wire. Verified against SDK 1.30.0 on 21 Sep 2026 — and the table is re-derived weekly, because normalisation changes are often undocumented and a check keyed to the version cannot fire when the behaviour changed and the version did not.
This is the right layer for the purpose. You are who MCPulse reports to, and “my code threw” versus “my code returned an error” is actionable for you in a way the wire is not. But these outcomes describe your code, not the caller’s experience, and it would be wrong to let them read as the same thing.
If you are on an SDK version not in that table, MCPulse shows you the
McpServer layer and says so. It does not interpolate from the nearest version it has tested.
crashed versus tool_error
These look identical from outsideMcpServer, which catches everything a tool does and converts it into { isError: true }. The SDK wraps your tool callbacks as well as the request handler so the difference survives.
It matters because the two have opposite fixes:
tool_error— your tool decided to refuse. Often correct behaviour. Worth checking the model understands why it was refused.crashed— your tool has a bug. Nothing about the model’s behaviour will fix it.
bad_args is a description problem
bad_args means the model constructed arguments your schema rejected, and your handler never ran. That is not a bug in your code; it is a gap between what your schema requires and what your description implies.
A tool with a high bad_args share almost always has an under-described required field, an enum whose values are not listed, or a name that suggests something other than what it takes.
The error rate
The error rate on the overview isbad_args + tool_error + crashed as a share of calls. empty is deliberately not in it — an empty result is a different failure with a different rate beside it.
Counted by name, in both tables
The tool table and the clients panel both carryBad args, Tool errors, Crashes and Empty as their own columns, from one shared definition — so a row means the same thing on both.
They were one column called Failed, and pooling is what made it useless. 13.4% failed says open the tool and nothing about what to change inside it. 389 bad arguments is a schema the model cannot fill in; 42 tool errors is a handler refusing work it was given; 3 crashes is a bug. One number, three fixes.
Counts, not rates. Both tables already carry calls, so the denominator is on the row — and a crash is an event rather than a proportion. Three of them reads as something to go and fix, where the same three as 0.1% is the figure your eye skips. A zero from real traffic is muted; a row with no calls dashes all four, because zero failures out of zero calls is not a measurement.
The one pooled figure left is the line above the outcome breakdown — “13.4% failed · 0.4% empty” — which is a summary sitting on top of a panel that lists all five by name underneath it.
Filtering
Outcomes scope cleanly to both filters:tool_hours carries per-outcome counters per tool, per client, per hour. So does the error rate, and so does everything derived from them.
Related
- Empty results
- First-call success — which counts
okand non-empty and un-retried - Tool detail — the same breakdown for one tool