- MCP puts tool errors in the response body, not the HTTP status.
- APM and log-based alerts stay green while agents silently retry and give up.
- Count isError responses per tool, separately from transport errors.
The most expensive failures on an MCP server are the ones nothing alerts on. The transport returns 200. Your APM shows healthy latency. But the tool inside that response failed, and the agent quietly moved on.
Why this happens
MCP encodes tool-level errors in the response body, often with an isError flag, rather than as an HTTP status. That is a reasonable protocol decision: a tool failing is not the same as the server failing. But it means every layer built around status codes is blind to it.
{
"content": [{ "type": "text", "text": "to: expected array" }],
"isError": true
}What the agent does next
It rarely surfaces the error to the user. It retries, sometimes with the same malformed arguments, then abandons the task. From the user's side the assistant just 'didn't do it.' From your side, everything looks fine.
How to catch it
- Read the payload, not just the status code
- Count isError responses per tool, separately from transport errors
- Track retries within a session to spot give-up loops
- Alert on error-rate spikes at the tool level
Once you separate transport health from tool health, send_email failing 94 percent of the time stops being invisible and becomes the first thing you fix.
See this on your own server
TrackMCP turns your MCP server's calls into adoption, workflows, and outcomes. One line to install.