Skip to content

Six MCP reads answer 'nothing happened' and 'nothing collected' with identical bytes, and three of them disagree between the SKUs #2485

Description

@erikdarlingdata

The MCP half of a defect just fixed on the web half in #2477.

get_blocking_trend serializes { server, hours_back, trend } unconditionally. On a server that had no blocking, trend is [] — the same bytes a server with nothing collected yet produces. There is no {status, message} envelope, no count, nothing.

That was visible on the web dashboard because renderPanel had real data in hand and fell through to the chart's "Not enough data points to chart yet" — a warming-up message on a server that simply never blocked. #2477 fixes the browser at the renderer.

It is worse for an agent than it was for a person. A human sees an empty chart in a page whose other panels are populated and infers the server is fine. An MCP client has only the JSON. An LLM asked "is this server blocking?" can reasonably answer "there is no data" when the true answer is "no, and that is good news" — and on a monitoring tool that is close to the worst available failure, because it converts a clean bill of health into an apparent gap in coverage.

Scope of the pattern

Scanned every [McpServerTool] in Darling/PerformanceMonitor.Darling.Service/Mcp and Lite/Mcp for a top-level collection with no early-return on empty.

Darling — no distinction between "none happened" and "none collected":

Tool Empty shape Web read?
get_blocking_trend trend: [] yes
get_deadlock_trend trend: [] yes
get_memory_clerks clerks: [] yes
get_wait_types wait_types: [] yes
get_mute_rules bare list yes
compare_analysis bare comparison yes

Neighbours that already do this correctly, which is what makes the six read as an omission rather than a decision: get_wait_stats, get_memory_stats, get_file_io_trend, get_memory_trend, get_query_duration_trend and ~50 others all return McpHelpers.Status("unavailable", "…"); the three *_changes tools go further and return a purpose-built NoChanges(...) carrying hints: { server, snapshot_count } — which is precisely the "queried successfully, zero rows, here is the denominator" shape the trend tools want; and list_servers guards its empty registry with prose. So the vocabulary exists and these did not adopt it.

Lite — the same six, plus three where the two SKUs disagree:

get_file_io_trend, get_memory_trend and get_query_duration_trend return a bare empty array on Lite while their Darling twins return a status envelope. Same tool name, same client, two different answers depending on which SKU it is pointed at. That is a parity break independent of this issue's main point and is arguably the more urgent half.

What the fix probably looks like

Not simply "add an envelope" — for a periodic sample, an absence is only interpretable with its denominator, which is the reasoning behind the PostgreSQL pg_blocking read carrying its own capture counts from collection_log: the edge table cannot tell "no blocking" from "not collected", both being an absence of rows, but the capture count can. get_blocking_trend has exactly that property.

So the shape worth converging on is the one NoChanges(...) already uses — a status plus hints naming what was examined — rather than a bare "unavailable", which would replace one ambiguity with a friendlier-sounding one. An agent needs to be able to tell "42 collection cycles ran in this window and none of them saw blocking" from "no collection cycles ran".

Both SKUs, and the shape has to match or the parity gap above just moves.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions