Repository navigation
Open
Labels
Description
Summary
Two cases where clickstack_table / clickstack_timeseries return a plausible-looking answer that is wrong, with no error or warning. Agents can't tell, so they build conclusions on them.
min/maxon a DateTime column return 0. Every non-count aggregate wraps its value intoFloat64OrDefault(toString(expr))(packages/common-utils/src/core/renderChartConfig.ts:540-543). ForDate/DateTime/DateTime64,toString()gives'2026-11-04 08:00:00.000000000', which doesn't parse as a float, so every value becomes 0 andmax(Timestamp)is 0.- Unknown options are dropped. The tool input schemas are plain
z.object(...)(e.g.tableSchemainpackages/api/src/mcp/tools/query/table.ts:32), so zod strips keys it doesn't know.having: "lines > 5"is accepted, the SQL has noHAVING, and rows with 1 come back. Agents tryhavingbecause dashboard tiles support it (packages/api/src/mcp/tools/dashboards/schemas.ts:839).
Environment
HyperDX 2-nightly image (late Oct 2026) with ClickHouse 26.10.3. Code references checked on main at d08b2fa (2026-11-03, 2.43.1).
Steps to reproduce
clickstack_tableon a log or trace source,select: [{aggFn: "min", valueExpression: "Timestamp", alias: "first_seen"}, {aggFn: "max", valueExpression: "Timestamp", alias: "last_seen"}], any time range with data.- Result:
first_seen: 0, last_seen: 0. The SQL insystem.query_logismax(toFloat64OrDefault(toString(Timestamp))).
The cast alone, on any ClickHouse:
WITH arrayJoin([toDateTime64('2026-11-04 07:59:01.123', 9), toDateTime64('2026-11-04 08:00:00', 9)]) AS Timestamp
SELECT max(toFloat64OrDefault(toString(Timestamp))) AS rendered, max(Timestamp) AS expected
-- rendered = 0, expected = 2026-11-04 08:00:00.000000000clickstack_tablewithgroupByandhaving: "count > 5": no validation error, the generated SQL has noHAVING, and groups with count 1 are returned.
Expected vs actual
| Expected | Actual | |
|---|---|---|
max(Timestamp) |
the latest timestamp (or a clear error) | 0 |
unknown option having |
a validation error naming the valid options, or having applied |
silently ignored |
Impact
In 4 agent investigations on production-size data (about 170 MCP calls), 3 asked "first seen / last seen" questions and got 0 back, and 2 used having and got unfiltered results (one call returned 1,044 groups, which then hit response trimming, see #3542).
Proposed fix
min,maxandanydon't need a Float64 value. Pass the expression through uncast for them, or convert dates numerically (e.g.toUnixTimestamp64Milli) instead of viatoString. The aggregate already handles numbers and numeric strings today, so a type check (date-like → no cast) keeps that behaviour.- Make the MCP tool input schemas
.strict(), so an unknown key returns a validation error listing the accepted options. Addinghavingandlimittoclickstack_tableitself, as tiles already support, is part of MCP response trimming replaces the largest values whole (often the top result) and ignores topN;clickstack_tablehas nolimit,havingor default order #3542.
Related
- MCP response trimming replaces the largest values whole (often the top result) and ignores topN;
clickstack_tablehas nolimit,havingor default order #3542: MCP response trimming, andclickstack_tablehas nolimit,havingor default order - Metric selects ignore
valueExpression: gauge and sum charts always aggregate the raw value #3499:valueExpressionignored on metric selects - count_distinct aggFn on metric tiles ignores valueExpression, always counts distinct metric Values #2311 / fix(common-utils): count distinct attribute values on gauge metric charts #3237:
count_distincton metric tiles
Activity
feat(mcp): limit, having and a default order for clickstack_table (#3558