About metrics-chart
metrics-chart is a Python CLI skill that turns metrics v2/v3 query responses into multi-series line charts for developers and agents inspecting time-series data. It selects image or ASCII output and reduces high-cardinality results by collapsing overlapping lines and limiting series.
Key features
- 01Read metrics v2/v3 JSON responses
- 02Plot multi-series time-series lines
- 03Output ASCII, PNG, SVG, or sixel
- 04Collapse overlapping lines and limit series
- 05Set local time or an explicit timezone
- 06Render ASCII charts with Python's standard library
Use cases
- 01Inspect service metrics over time
- 02View metric charts in terminals or CI logs
- 03Display saved MCP query results as images
MCP support
MCP queryMetrics results can be used as input; set truncate: false and save structuredContent. The project is a charting CLI, not an MCP server.
Install
Copy the command and run it in a terminal.
npx skills add https:/ / github.com/ axiomhq/ skills --skill metrics-chartAfter installing, type/metrics-chartin Claude Code to confirm it loaded.
Bundled files
References and scripts installed along with SKILL.md.
.gitignoreREADME.mdexamples/v3_gaps.jsonv3_golden.jsonv3_many.jsonscripts/metrics_chart.pytest_metrics_chart.pyDetails
From the project READMEmetrics-chart
Turns a metrics query response into a multi-series line chart.
Input is a saved {metadata, series} response in metrics v2 or v3 format. For MCP results, call queryMetrics with truncate: false and save its structuredContent as the input JSON. The default CSV preview can omit series and samples.
TL;DR
# From a file, stdout picks the best renderer for your terminal:
python3 scripts/metrics_chart.py response.json
# Read the same saved response from stdin:
python3 scripts/metrics_chart.py < response.json
# Force a zero-dependency ASCII chart (always works, anywhere):
python3 scripts/metrics_chart.py --format ascii response.json
Aggregate first. Charts are only readable with a handful of series. The cleanest result comes from a query that already groups/aggregates to a few lines (e.g.
group by statusrather than per-pod). This skill's series reduction (below) is a safety net for high-cardinality results, not a substitute for aggregating in the query.
Input format (metrics v2/v3)
{
"metadata": {
"group_keys": ["code", "method"],
"warnings": [],
"unit": "ms",
"custom_unit": "millisec"
},
"series": [
{
"metric": "http_requests",
"tags": { "code": 200, "method": "GET", "path": "/" },
"start": 1750753164,
"resolution": 60,
"data": [420.0, 42.0, 30.0, 9.0, 15.0, 18.0]
}
]
}
| Field | Meaning |
|---|---|
metadata.group_keys | Tags the query grouped on → used to build legend labels. |
metadata.warnings | Shown as ⚠ captions under the chart. |
metadata.unit | Canonical y-axis unit (ms, bytes, …), unless ignore_unit is true. |
metadata.custom_unit | Human y-axis label; overrides unit when supplied. |
metadata.ignore_unit | Discard unit; only an explicitly supplied custom_unit applies. |
series[].start | Unix seconds (UTC) of data[0]. |
series[].resolution | Seconds per point; x[i] = start + resolution*i (uniform spacing). |
series[].data | Y values; null = gap (the line breaks, it is not drawn as 0). |
The parser also accepts v2/v2a (same shape with an extra per-series summary,
ignored) and the v0/v1 bare-array form, so you can feed older responses too.
Behaviour
- Labels come from
group_keysvalues joined with|→200 | GET(notcode=200,method=GET). Withoutgroup_keys, falls back tok=vtags. - Local time axis by default;
--tz UTCor--tz Europe/Berlinto override. - Gaps:
nullpoints break the line. - Series reduction (unless
--all), in order:- Collapse overlapping series whose lines essentially coincide (within
--eps, default 2% of the y-range); the most prominent is kept. - Top-N by peak value (
--top, default 8). - The legend prints how many were dropped, e.g.
showing 8 of 30 series (5 overlapping, 17 low/hidden), plus the aggregate-your-query tip.
- Collapse overlapping series whose lines essentially coincide (within
Output formats
--format auto (default) picks the best renderer for the context:
- an inline image when
gnuplotis installed and the terminal supports images (Kitty/Ghostty, iTerm2); - a PNG file plus a "display this file" instruction when output is
piped/captured (e.g. an AI agent harness) and
gnuplotis installed — the agent then displays the file; - ASCII in a plain terminal, or whenever
gnuplotis unavailable.
Force a specific backend with --format:
--format | Needs | Output |
|---|---|---|
auto | — | inline image, else a PNG file + display instruction when piped, else ASCII |
ascii | nothing | Unicode braille line chart; renders in any terminal/transcript/CI log. |
png | gnuplot | PNG; displayed inline (Kitty/iTerm2) or written to --output/temp. |
svg | gnuplot | SVG written to --output or a temp file (path printed). |
sixel | gnuplot | sixel stream to stdout (terminals with sixel support). |
If an image format is requested but gnuplot is missing, it prints a note and
falls back to ASCII — it never hard-fails.
Using from an AI agent / harness
Terminal harnesses (e.g. pi) collapse tool output and don't repaint inline
ANSI, so ASCII charts come out monochrome and folded. Prefer images: render
a file and let the agent display it. With --format auto (the default) this is
automatic whenever output is piped and gnuplot is installed — the skill
writes a PNG and prints:
metrics-chart: wrote image to /tmp/metrics-chart-XXXX.png
→ Display this file to the user with your image tool (e.g. read it); do not paste its contents.
The agent should then display that file (its image/read tool renders it
inline, in colour, un-folded). To control the path, pass --output chart.png.
Do not paste the PNG bytes or the ASCII into chat.
Options
| Flag | Default | Meaning |
|---|---|---|
--format | auto | auto/ascii/png/svg/sixel. |
--tz NAME | local | IANA tz for the x-axis; UTC for UTC. |
--top N | 8 | Max series after collapse; must be >= 1. |
--eps F | 0.02 | Overlap threshold as a fraction of the y-range. |
--all | off | Draw every series (disable collapse + top-N). |
--width / --height | auto | Chart size (cells for ASCII, pixels for images). |
--title TEXT | — | Chart title. |
--output PATH | — | Write png/svg to PATH instead of a temp file. |
--color / --no-color | auto | Force ANSI colour on/off (default: on when a TTY). |
Dependencies
- Default path: none. Pure Python 3.9+ standard library. The ASCII renderer always works.
- Optional:
gnuplot(one system binary) for PNG/SVG/sixel. Install only if you want high-fidelity images.
Why not graphviz?
graphviz (dot/neato) lays out node-edge graphs; it has no numeric/time
axis and cannot plot data[i] over time. This payload is a numeric time series
with no graph structure, so graphviz does not apply. gnuplot is the correct
image backend. (matplotlib is avoided on purpose: it is a heavy multi-package
pip install.)
Tests
cd scripts && python3 -m unittest test_metrics_chart -v
The parser tests are pinned to the golden bytes from the metrics query
service's service/query/src/server/tests.rs.