graphein-mcp 0.12.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "graphein-mcp",
3
- "version": "0.12.0",
3
+ "version": "0.14.0",
4
4
  "type": "module",
5
5
  "description": "Model Context Protocol server for Graphein — wraps generate → validate → repair → render → critique into one tool call, and serves Graphein's schema + agent guide as resources so a model that never saw the API can still build correct charts.",
6
6
  "license": "MIT",
@@ -61,9 +61,9 @@
61
61
  "prepack": "tsup"
62
62
  },
63
63
  "dependencies": {
64
- "@graphein/node": "^0.12.0",
64
+ "@graphein/node": "^0.14.0",
65
65
  "@modelcontextprotocol/sdk": "^1.20.0",
66
- "graphein": "^0.12.0",
66
+ "graphein": "^0.14.0",
67
67
  "zod": "^3.23.0"
68
68
  }
69
69
  }
@@ -111,7 +111,7 @@ reference: [spec-reference → Transforms](./spec-reference.md#transforms).
111
111
  | Running total / bridge | `waterfall` | `stage`, `value` (signed deltas) |
112
112
  | Before / after by series | `slope` | `x`, `y`, `series` |
113
113
  | Gap between two groups | `dumbbell` | `category`, `value`, `group` |
114
- | Headline metric | `kpi` | `value`, `delta`, `sparkline` |
114
+ | Single number / headline metric | `kpi` | `value` (+ `label`, `delta`, `sparkline`, `comparisons`) |
115
115
  | Raw/detail records | `table` | `columns` (+ optional totals, groups, bars/icons/rules) |
116
116
  | Aggregated cross‑tab | `matrix` | `rows`, `columns`, `values` (+ `showAs` percentages) |
117
117
  | Slice/filter a field | `dropdown` · `list` · `search` · `range` · `dateRange` | `field` (+ `param?`) |
@@ -354,20 +354,32 @@ Don't pre-bin or pre-count — feed one row per observation and let `bin` do the
354
354
  }
355
355
  ```
356
356
 
357
- **KPI with delta + sparkline**
357
+ **KPI — scorecard (delta + sparkline + comparison rows)**
358
+
359
+ The `kpi` is the only single-number visual. With just a `value` (+ `label`) it degrades to
360
+ a clean centered **card**; add any of `delta`, `sparkline`, or `comparisons` and it becomes
361
+ a full scorecard (label + value + ▲/▼ delta, an inline trend with min/max/last markers, and
362
+ a divider over comparison rows).
358
363
 
359
364
  ```jsonc
360
365
  {
361
366
  "type": "kpi",
362
- "label": "Total sales",
363
- "value": { "field": "sales", "aggregate": "sum" },
364
- "delta": 0.124, // +12.4%, drives the up indicator
365
- "format": "$,.0f",
366
- "sparkline": true,
367
- "data": [/* rows with a `sales` column */]
367
+ "label": "Total Costs AC",
368
+ "value": { "field": "cost", "aggregate": "sum" },
369
+ "format": ",.0f",
370
+ "sparkline": { "field": "cost", "markers": true }, // min→red, max→green, last→ring
371
+ "comparisons": [ // each row: sign drives ▲/▼ + color
372
+ { "label": "BU", "delta": -0.02, "amount": -268 }, // ▼ -2% : -268 (red)
373
+ { "label": "PY", "delta": 0.02, "amount": 295 }, // ▲ 2% : 295 (green)
374
+ { "label": "MoM", "delta": 0.01, "amount": 96 } // ▲ 1% : 96 (green)
375
+ ],
376
+ "data": [/* rows with a `cost` column */]
368
377
  }
369
378
  ```
370
379
 
380
+ A minimal card is just `{ "type": "kpi", "value": { "field": "revenue", "aggregate": "last" }, "label": "Revenue", "unit": "B" }`.
381
+
382
+
371
383
  **Table with conditional formatting**
372
384
 
373
385
  ```jsonc
@@ -2492,6 +2492,16 @@
2492
2492
  "$ref": "#/$defs/DashboardResponsiveSpan"
2493
2493
  },
2494
2494
  "description": "Per-view responsive span overrides; smallest matching maxWidth wins."
2495
+ },
2496
+ "interact": {
2497
+ "type": "string",
2498
+ "enum": [
2499
+ "auto",
2500
+ "none",
2501
+ "source",
2502
+ "target"
2503
+ ],
2504
+ "description": "This view's role in `interactions: 'auto'` cross-interaction. Default: full participation (it both drives and reacts).\n- 'none': inert — never filters/highlights others and never reacts.\n- 'source': can drive filters/highlights but is never filtered or dimmed.\n- 'target': reacts to others but never drives (e.g. a read-only detail chart). Ignored when `interactions` is `'none'` or an explicit InteractionLink array."
2495
2505
  }
2496
2506
  },
2497
2507
  "required": [
@@ -3729,14 +3739,8 @@
3729
3739
  "description": "Accessible description (alt text) for the chart. Used verbatim as the chart's `aria-label`; when omitted, a concise label is synthesized from the type, title, and data. Agents should set this to convey the chart's intent."
3730
3740
  },
3731
3741
  "legend": {
3732
- "anyOf": [
3733
- {
3734
- "$ref": "#/$defs/LegendConfig"
3735
- },
3736
- {
3737
- "type": "boolean"
3738
- }
3739
- ]
3742
+ "type": "boolean",
3743
+ "description": "Show a legend naming the value / comparison / track arcs (top-right). Defaults to shown when `compare` is set; otherwise hidden."
3740
3744
  },
3741
3745
  "tooltip": {
3742
3746
  "anyOf": [
@@ -3826,7 +3830,11 @@
3826
3830
  },
3827
3831
  "target": {
3828
3832
  "$ref": "#/$defs/ValueRef",
3829
- "description": "Optional target/threshold marker drawn as a needle/tick."
3833
+ "description": "Optional target/threshold marker drawn as a subtle tick across the arc."
3834
+ },
3835
+ "compare": {
3836
+ "$ref": "#/$defs/ValueRef",
3837
+ "description": "Optional comparison value (e.g. a prior period) drawn as a thinner concentric arc just inside the main track."
3830
3838
  },
3831
3839
  "label": {
3832
3840
  "type": "string",
@@ -3834,7 +3842,7 @@
3834
3842
  },
3835
3843
  "format": {
3836
3844
  "type": "string",
3837
- "description": "Number format for the value + scale ticks (e.g. ',.0f', '.0%')."
3845
+ "description": "Number format for the value readout (e.g. ',.0f', '.0%')."
3838
3846
  },
3839
3847
  "bands": {
3840
3848
  "type": "array",
@@ -3853,7 +3861,19 @@
3853
3861
  ],
3854
3862
  "additionalProperties": false
3855
3863
  },
3856
- "description": "Qualitative arc bands, each filling the scale up to `to`."
3864
+ "description": "Qualitative arc bands — recolor the fill by the zone the value lands in."
3865
+ },
3866
+ "valueLabel": {
3867
+ "type": "string",
3868
+ "description": "Legend label for the value (fill) arc. Default 'Actual'."
3869
+ },
3870
+ "compareLabel": {
3871
+ "type": "string",
3872
+ "description": "Legend label for the comparison arc. Default 'Prior period'."
3873
+ },
3874
+ "trackLabel": {
3875
+ "type": "string",
3876
+ "description": "Legend label for the full-scale track. Default 'Target'."
3857
3877
  }
3858
3878
  },
3859
3879
  "required": [
@@ -3862,7 +3882,7 @@
3862
3882
  "max"
3863
3883
  ],
3864
3884
  "additionalProperties": false,
3865
- "description": "Gauge — a radial dial showing a single value against a `[min, max]` scale, with an optional `target` needle and qualitative background `bands`. The value is a literal or a field (optionally aggregated, like KpiSpec )."
3885
+ "description": "Gauge — a clean semicircular radial-progress dial showing one value against a `[min, max]` scale. The full track is the scale (\"Target\"); an accent arc fills from the start to `value` (\"Actual\"); an optional `compare` value (e.g. a prior period) draws as a thinner concentric inner arc. A big value readout sits in the middle with `label` beneath it, and an optional `legend` names the arcs. `value`/`target`/`compare` are literals or fields (optionally aggregated, like KpiSpec ). Qualitative `bands` recolor the fill by the zone the value lands in, and `target` draws a subtle threshold tick."
3866
3886
  },
3867
3887
  "GeoFeature": {
3868
3888
  "type": "object",
@@ -4537,6 +4557,41 @@
4537
4557
  "additionalProperties": false,
4538
4558
  "description": "Explicit cross-interaction from one view to others (overrides auto-wiring)."
4539
4559
  },
4560
+ "KpiComparison": {
4561
+ "type": "object",
4562
+ "properties": {
4563
+ "label": {
4564
+ "type": "string",
4565
+ "description": "Short caption shown before the indicator (e.g. 'BU', 'PY', 'MoM')."
4566
+ },
4567
+ "delta": {
4568
+ "$ref": "#/$defs/ValueRef",
4569
+ "description": "The change — its sign drives the ▲/▼ indicator and green/red color."
4570
+ },
4571
+ "amount": {
4572
+ "$ref": "#/$defs/ValueRef",
4573
+ "description": "Absolute amount shown after the percentage (e.g. `: -268`)."
4574
+ },
4575
+ "format": {
4576
+ "type": "string",
4577
+ "description": "Format for `delta` (default: signed percent, e.g. `-0.02` → `-2%`)."
4578
+ },
4579
+ "amountFormat": {
4580
+ "type": "string",
4581
+ "description": "Format for `amount` (default: the KPI's `format`, else a plain integer)."
4582
+ },
4583
+ "invert": {
4584
+ "type": "boolean",
4585
+ "description": "Flip the color semantics so a *decrease* reads as good (green ▼)."
4586
+ }
4587
+ },
4588
+ "required": [
4589
+ "label",
4590
+ "delta"
4591
+ ],
4592
+ "additionalProperties": false,
4593
+ "description": "A named comparison row on a KpiSpec (e.g. vs. budget, prior year, month-over-month). The sign of `delta` drives the ▲/▼ indicator and its positive/negative color; an optional `amount` shows the absolute change after the percentage (`BU ▼ -2%: -268`)."
4594
+ },
4540
4595
  "KpiSpec": {
4541
4596
  "type": "object",
4542
4597
  "properties": {
@@ -4678,14 +4733,16 @@
4678
4733
  "description": "Literal value or a field (optionally aggregated over data)."
4679
4734
  },
4680
4735
  "label": {
4681
- "type": "string"
4736
+ "type": "string",
4737
+ "description": "Caption shown with the value."
4738
+ },
4739
+ "format": {
4740
+ "type": "string",
4741
+ "description": "d3-format string for the value (e.g. ',.0f', '$,.2f', '.1%')."
4682
4742
  },
4683
4743
  "delta": {
4684
4744
  "$ref": "#/$defs/ValueRef",
4685
- "description": "Delta vs. a comparison, drives the up/down indicator."
4686
- },
4687
- "format": {
4688
- "type": "string"
4745
+ "description": "Primary delta vs. a comparison — drives an ▲/▼ indicator under the value."
4689
4746
  },
4690
4747
  "sparkline": {
4691
4748
  "anyOf": [
@@ -4697,22 +4754,55 @@
4697
4754
  "properties": {
4698
4755
  "field": {
4699
4756
  "type": "string"
4757
+ },
4758
+ "markers": {
4759
+ "type": "boolean"
4700
4760
  }
4701
4761
  },
4702
- "required": [
4703
- "field"
4704
- ],
4705
4762
  "additionalProperties": false
4706
4763
  }
4707
4764
  ],
4708
- "description": "Inline sparkline from a numeric field."
4765
+ "description": "Inline trend from a numeric field. `true` uses the value's field; an object names the `field` and can toggle the min/max/last `markers` (default true)."
4766
+ },
4767
+ "comparisons": {
4768
+ "type": "array",
4769
+ "items": {
4770
+ "$ref": "#/$defs/KpiComparison"
4771
+ },
4772
+ "description": "Named comparison rows (vs. budget, prior year, MoM …) beneath the value."
4773
+ },
4774
+ "align": {
4775
+ "type": "string",
4776
+ "enum": [
4777
+ "start",
4778
+ "center",
4779
+ "end"
4780
+ ],
4781
+ "description": "Horizontal alignment of the value + label block. Default 'center'."
4782
+ },
4783
+ "labelPosition": {
4784
+ "type": "string",
4785
+ "enum": [
4786
+ "above",
4787
+ "below"
4788
+ ],
4789
+ "description": "Where the label sits relative to the value. Defaults to 'above' when the KPI has a delta/sparkline/comparisons, otherwise 'below' (the plain-card look)."
4790
+ },
4791
+ "unit": {
4792
+ "type": "string",
4793
+ "description": "Optional unit/suffix drawn after the value (e.g. '%', 'ms', 'MB')."
4794
+ },
4795
+ "color": {
4796
+ "type": "string",
4797
+ "description": "Value color override (defaults to the theme text color)."
4709
4798
  }
4710
4799
  },
4711
4800
  "required": [
4712
4801
  "type",
4713
4802
  "value"
4714
4803
  ],
4715
- "additionalProperties": false
4804
+ "additionalProperties": false,
4805
+ "description": "KPI — the value tile, and the *only* single-number visual. At its simplest it is one big number with an optional caption (a \"card\"); add a `delta`, a `sparkline`, and/or `comparisons` and it grows into a full scorecard. It supports horizontal `align`, a `label` placed `above`/`below`, a `unit` suffix, and a value `color`."
4716
4806
  },
4717
4807
  "LegendConfig": {
4718
4808
  "type": "object",
@@ -595,15 +595,34 @@ Dense category × category grid colored by a measure.
595
595
 
596
596
  ### kpi
597
597
 
598
- A single stat card: big value, label, delta indicator, and inline sparkline.
598
+ The value tile — and the **only** single-number visual. At its simplest it degrades to a
599
+ plain **card**: one big centered number with an optional caption. Add a `delta`, a
600
+ `sparkline`, and/or `comparisons` and it grows into a full **scorecard** — a header (label
601
+ + value + ▲/▼ delta), an inline trend with min/max/last markers, and a divider over a
602
+ stack of comparison rows (`BU ▼ -2% : -268`).
599
603
 
600
604
  | Field | Type | Notes |
601
605
  | --- | --- | --- |
602
- | `value` | `number \| { field, aggregate? }` | A literal, or a field aggregated over `data`. |
603
- | `label` | `string` | Caption under/above the value. |
604
- | `delta` | `number \| { field, aggregate? }` | Drives the up/down indicator (e.g. `0.124` → +12.4%). |
606
+ | `value` | `number \| { field, aggregate? }` | **Required.** A literal, or a field aggregated over `data`. |
607
+ | `label` | `string` | Caption shown with the value. |
605
608
  | `format` | `string` | [Format hint](#format-mini-language) for the value. |
606
- | `sparkline` | `boolean \| { field }` | Inline trend from a numeric field. |
609
+ | `delta` | `number \| { field, aggregate? }` | Drives the primary ▲/▼ indicator under the value (e.g. `0.124` → +12.4%). |
610
+ | `sparkline` | `boolean \| { field?, markers? }` | Inline trend from a numeric field (`true` uses the value's field). `markers` toggles the min/max/last dots (default `true`). |
611
+ | `comparisons` | `KpiComparison[]` | Named comparison rows beneath a divider (vs. budget, prior year, MoM …). See below. |
612
+ | `align` | `'start' \| 'center' \| 'end'` | Horizontal alignment of value + label. Default `'center'`. |
613
+ | `labelPosition` | `'above' \| 'below'` | Where the label sits relative to the value. Defaults to `'above'` when rich (delta/sparkline/comparisons), else `'below'` (the plain-card look). |
614
+ | `unit` | `string` | Optional suffix drawn after the value (e.g. `'%'`, `'B'`, `'ms'`). |
615
+ | `color` | `string` | Value color override (defaults to the theme text color). |
616
+
617
+ **`KpiComparison`** — one row: `{ label, delta, amount?, format?, amountFormat?, invert? }`.
618
+ The sign of `delta` drives the ▲/▼ glyph and its green/red color; `amount` shows the
619
+ absolute change after the percentage (`BU ▼ -2% : -268`). `format`/`amountFormat` are
620
+ [format hints](#format-mini-language) (default: signed percent, then the KPI's `format`);
621
+ `invert: true` flips the color so a *decrease* reads as good.
622
+
623
+ > **Degrades to a card:** with only a `value` (+ optional `label`), the KPI renders as a
624
+ > clean centered number — reach for this when you want *just the headline metric*. A field
625
+ > value may be aggregated (e.g. `aggregate: 'last'` shows the most recent row).
607
626
 
608
627
  → [`examples/kpi.json`](./examples/kpi.json)
609
628
 
@@ -754,19 +773,28 @@ tiles, each with a header label.
754
773
 
755
774
  ### gauge
756
775
 
757
- A radial dial showing one value against a `[min, max]` scale, with an optional `target`
758
- tick and qualitative background `bands`. Like [`kpi`](#kpi), the value is a literal
759
- **or** a field (optionally aggregated over `data`). Renders to canvas (headless‑safe).
776
+ A clean **semicircular radial‑progress dial** showing one value against a `[min, max]`
777
+ scale. The full rounded track is the scale (**"Target"**); an accent arc fills from the
778
+ start to `value` (**"Actual"**); an optional `compare` value (e.g. a prior period) draws
779
+ as a thinner concentric inner arc (**"Prior period"**). A big value readout sits in the
780
+ middle with `label` beneath it, and an optional `legend` (top‑right) names the arcs. Like
781
+ [`kpi`](#kpi), `value`/`target`/`compare` are literals **or** fields (optionally aggregated
782
+ over `data`). Renders to canvas (headless‑safe).
760
783
 
761
784
  | Field | Type | Notes |
762
785
  | --- | --- | --- |
763
786
  | `value` | `ValueRef` | The measured value: a literal number, or `{ field, aggregate? }` summarized over `data`. |
764
787
  | `max` | `number` | **Required.** Scale end (full‑scale). |
765
788
  | `min` | `number` | Scale start (default `0`). |
766
- | `target` | `ValueRef` | Optional threshold drawn as a needle/tick. |
767
- | `bands` | `{ to: number, color? }[]` | Qualitative arc bands, each filling the scale up to `to`. |
789
+ | `target` | `ValueRef` | Optional threshold drawn as a subtle tick across the arc. |
790
+ | `compare` | `ValueRef` | Optional comparison value (e.g. a prior period) drawn as a thinner concentric inner arc. |
791
+ | `bands` | `{ to: number, color? }[]` | Qualitative bands — recolor the value fill by the zone the value lands in. |
768
792
  | `label` | `string` | Caption under the value (defaults to the title or value field). |
769
- | `format` | `string` | Number format for the value + scale ticks (e.g. `,.0f`, `.0%`). |
793
+ | `format` | `string` | Number format for the value readout (e.g. `,.0f`, `.0%`). |
794
+ | `legend` | `boolean` | Show a legend naming the arcs (top‑right). Defaults to shown when `compare` is set. |
795
+ | `valueLabel` | `string` | Legend label for the value (fill) arc. Default `'Actual'`. |
796
+ | `compareLabel` | `string` | Legend label for the comparison arc. Default `'Prior period'`. |
797
+ | `trackLabel` | `string` | Legend label for the full‑scale track. Default `'Target'`. |
770
798
 
771
799
  → [`examples/gauge.json`](./examples/gauge.json)
772
800
 
@@ -1029,7 +1057,7 @@ views out on a responsive grid, and **auto‑wires** cross‑interaction. Valida
1029
1057
  | `background` | `string` | transparent | Header band tint. |
1030
1058
  | `collapsed` | `boolean` | `false` | Start with the body hidden behind a clickable header. |
1031
1059
 
1032
- **`DashboardView`** — `{ id, spec, x?, y?, w?, h?, title?, subtitle?, frame?, background?, accent?, padding?, responsive? }`.
1060
+ **`DashboardView`** — `{ id, spec, x?, y?, w?, h?, title?, subtitle?, frame?, background?, accent?, padding?, responsive?, interact? }`.
1033
1061
  `id` is unique within the dashboard (used for layout + link references). `spec` is any
1034
1062
  chart or slicer spec (inherits the dashboard's `data` when it has none). `x`/`y` are
1035
1063
  1‑based grid placement (omit to auto‑flow); `w`/`h` are column/row spans (sensible
@@ -1043,6 +1071,7 @@ per‑type defaults).
1043
1071
  | `accent` | `string` | — | Solid left accent bar color. |
1044
1072
  | `padding` | `'none' \| 'standard'` | `'standard'` | Use `'none'` for flush tables/maps. |
1045
1073
  | `responsive` | `{ maxWidth, w?, h?, hidden? }[]` | — | Per-view span overrides at section/dashboard widths; smallest matching `maxWidth` wins. |
1074
+ | `interact` | `'auto' \| 'none' \| 'source' \| 'target'` | `'auto'` | Role in auto cross-interaction. `'none'` = inert (never drives or reacts); `'source'` = drives filters but is never filtered/dimmed itself; `'target'` = reacts to others but never drives. |
1046
1075
 
1047
1076
  **`interactions: 'auto'`** (Power BI semantics):
1048
1077
 
@@ -1338,12 +1367,19 @@ mirror this surface.
1338
1367
  ### Animation
1339
1368
 
1340
1369
  Charts play a brief **entrance animation** the first time they render; **resizes**
1341
- are always instant (no re‑animation, no jank while dragging):
1342
-
1343
- - **Cartesian charts** (line/area/bar/scatter/box/heatmap) sweep their marks in
1344
- left‑to‑right with a short fade — the axes, gridlines, and labels are drawn
1345
- immediately so only the data "draws on".
1346
- - **Pie, funnel, KPI, sankey, choropleth, tables** fade and rise in subtly.
1370
+ are always instant (no re‑animation, no jank while dragging). Each family uses the
1371
+ motion that best fits its marks — and every entrance's **final frame is
1372
+ pixel‑identical to a static draw**:
1373
+
1374
+ - **Line / area / scatter / box** sweep their marks in left‑to‑right with a short
1375
+ fade — axes, gridlines, and labels draw immediately so only the data "draws on".
1376
+ - **Bars grow out of the value baseline** (both grouped and stacked; horizontal bars
1377
+ grow from the left).
1378
+ - **Pie / donut / gauge arcs sweep in** radially (the gauge fill arc and readout climb
1379
+ from the minimum to the value).
1380
+ - **KPI numbers count up** from 0 to the value (the box is sized from the final
1381
+ number, so the layout never reflows mid‑count).
1382
+ - **Funnel, sankey, choropleth, heatmap, treemap, tables** fade and rise in subtly.
1347
1383
 
1348
1384
  On **`update()`** (new data or config), canvas‑mark charts
1349
1385
  (line/area/bar/scatter/box/pie/heatmap/sankey/choropleth) **cross‑fade** the marks
@@ -1352,6 +1388,11 @@ pixel‑identical to an instant redraw. DOM charts (`kpi`/`table`/`matrix`) upda
1352
1388
  instantly, and a simultaneous size change snaps (that's a resize, not a data
1353
1389
  morph) to avoid a stretched bitmap.
1354
1390
 
1391
+ > All entrance styles are automatic per chart type — there's no per‑motion spec field
1392
+ > to set. *Future work:* tweening the dim/highlight on a selection change and rolling
1393
+ > `kpi` numbers from the previous value on `update()` (today they snap; the
1394
+ > entrance count‑up covers first render, reloads, and remounts).
1395
+
1355
1396
  Tuning via the `animation` field:
1356
1397
 
1357
1398
  ```jsonc