@bicharts/chart-mcp 0.6.91 → 0.6.93

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": "@bicharts/chart-mcp",
3
- "version": "0.6.91",
3
+ "version": "0.6.93",
4
4
  "description": "MCP (Model Context Protocol) stdio server for BIC AI charts: profile a dataset locally, then generate chart code that has passed the BIC backend's render gates. Works with any MCP-capable client (Claude Code, Claude Desktop, Cursor, Copilot Studio). An account pays to author charts; the code it writes is yours and runs with no licence.",
5
5
  "keywords": [
6
6
  "mcp",
@@ -47,8 +47,8 @@
47
47
  },
48
48
  "// dependencies": "NONE, deliberately. dist/ bundles every module the server uses, so a runtime dependency installs nothing it needs - it only makes npx fetch that package (and its tree) before the first start. Everything the bundle carries is a devDependency; scripts/verify-package.mjs fails the release if a dependency appears or a shipped file imports anything but Node's built-ins.",
49
49
  "devDependencies": {
50
- "@bicharts/chart-host": "^0.6.91",
51
- "@bicharts/shape-core": "^0.6.91",
50
+ "@bicharts/chart-host": "^0.6.93",
51
+ "@bicharts/shape-core": "^0.6.93",
52
52
  "@modelcontextprotocol/sdk": "^1.0.0",
53
53
  "@types/node": "^20.0.0",
54
54
  "@types/papaparse": "^5.3.14",
@@ -33,6 +33,7 @@ on the same picture, not a looser one.
33
33
  | Bivariate World Choropleth | 1 categorical, 2 continuous, geo: country-iso3/country-iso2/country-name | <=250 categories, needs 400x300 px | D3 | Best for two country-level measures that might disagree: wealth vs inequality, revenue vs margin, cases vs deaths - at least 24 countries and two measures that do not simply track each other |
34
34
  | Box plot | 1 continuous | needs 460x340 px | D3, Matplotlib, Plotly, Seaborn | Requires one continuous variable (plus optional category) |
35
35
  | Bubble chart | 3 continuous | needs 520x380 px | D3, Matplotlib, Plotly, Seaborn | Three continuous variables (x, y, size); one mark per row on continuous axes. If you want both axes grouped into bands with one aggregated bubble per cell, pick Binned bubble matrix instead |
36
+ | Budget allocator | 1 categorical, 2 continuous | needs 560x380 px | D3 | One spend measure and one outcome by channel and period - monthly spend and signups for five marketing channels, budget and sales by region - with at least eight periods per channel |
36
37
  | Bullet chart | 1 continuous, a reference value | needs 220x120 px | D3, PLOTLY | Best for actual-vs-target KPIs with thresholds (revenue vs goal, SLA vs target) — a denser, clearer replacement for gauges |
37
38
  | Bump chart | 1 categorical, 1 continuous, 3+ time points | <=15 categories, needs 320x280 px | D3, PLOTLY | Best for leaderboard/position dynamics: how a handful of regions, products, or teams change rank quarter to quarter |
38
39
  | Calendar heatmap | 1 continuous, 60+ time points | needs 600x420 px | D3, Matplotlib, Plotly, Seaborn | Requires daily datetime data with associated numeric values; best for activity/contribution tracking over time |
@@ -46,6 +47,7 @@ on the same picture, not a looser one.
46
47
  | Clustered heatmap | 2 categorical, 1 continuous | <=50 categories, needs 320x320 px | Plotly | Requires two variables for matrix values |
47
48
  | Connected scatterplot | 2 continuous, 6+ time points | needs 300x300 px | D3, PLOTLY | Best for the joint trajectory of two related metrics over time (e.g. units vs revenue, unemployment vs inflation) where the path shape is the insight |
48
49
  | Custom small multiples | 1 categorical, 1 continuous | <=16 categories, needs 320x240 px | D3, Matplotlib, Plotly, Seaborn | Best when comparing patterns across categories |
50
+ | Cutoff tuner | 1 continuous | needs 560x360 px | D3 | One row per case with a model score or rating and the actual yes/no outcome - churn, fraud, default, conversion - when the question is where to draw the line and what each choice costs |
49
51
  | Cycle plot | 1 continuous, 24+ time points | needs 320x280 px | D3, PLOTLY | Best for multi-year monthly/quarterly/weekly data where the question is seasonality vs trend ('is Q4 always highest, and is each Q4 growing?') |
50
52
  | Delta KPI | 2 continuous | needs 200x120 px | D3 | Two comparable measures, or one measure at two points in time - this period against last, actual against plan, after against before |
51
53
  | Dendrogram (tree diagram) | 2 categorical, 1 continuous | <=60 leaves, needs 320x240 px | D3, Plotly | Best for 2-4 hierarchy levels with ≤ ~80 leaves |
@@ -314,14 +314,20 @@ export async function createGroup(sampleUrl, dotnet) {
314
314
  const group = createChartGroup(cols, rows);
315
315
  const off = group.onChange(sel => dotnet.invokeMethodAsync("OnSelectionChanged", sel.sourceId, [...sel.rows]));
316
316
  return {
317
- async mount(el, spec) { // spec: { id, dir, height, respondsWith, geoKind }
317
+ async mount(el, spec) { // spec: { id, dir, height, respondsWith, geoKind, lasso }
318
318
  const code = await text(`${spec.dir}/chart.js`); // TEXT: chart-host compiles it and injects d3
319
319
  if (spec.geoKind) await loadGeo(spec.geoKind); // BEFORE the first render: render() is synchronous
320
320
  const opts = spec.respondsWith ? { respondsWith: spec.respondsWith } : {};
321
+ // The host lasso, for a chart whose generate_chart result carries capabilities.lasso (spec.lasso): a drag around
322
+ // marks selects the ones inside, through the group's own selection, so there is no handler to write. `capable` is
323
+ // the server's word for this chart; without it there is no lasso.
324
+ const lasso = spec.lasso
325
+ ? { capable: true, enabled: true, ...(spec.lasso.rowCap > 0 ? { rowCap: spec.lasso.rowCap } : {}) }
326
+ : null;
321
327
  const host = createChartHost(el, {
322
328
  code, d3, geoKind: spec.geoKind || undefined,
323
329
  data: group.memberPayload(spec.id, opts).payload,
324
- options: { width: Math.floor(el.getBoundingClientRect().width), height: spec.height, ...theme(el) },
330
+ options: { width: Math.floor(el.getBoundingClientRect().width), height: spec.height, ...theme(el), ...(lasso ? { lasso } : {}) },
325
331
  layoutPass: createLegendFitPass(), // after every render: a legend slid clear of the plot, text brought inside the frame
326
332
  });
327
333
  host.render();
@@ -428,6 +434,10 @@ export async function createGroup(sampleUrl, dotnet) {
428
434
  [Parameter] public string? RespondsWith { get; set; }
429
435
  /// <summary>A map's geo.kind from the generate_chart result (e.g. "us-state-name").</summary>
430
436
  [Parameter] public string? GeoKind { get; set; }
437
+ /// <summary>True when the generate_chart result carries capabilities.lasso: a drag around marks selects the ones inside.</summary>
438
+ [Parameter] public bool Lasso { get; set; }
439
+ /// <summary>capabilities.lasso.rowCap from the same result, when it gives one: the most rows in view that still get a lasso.</summary>
440
+ [Parameter] public int? LassoRowCap { get; set; }
431
441
 
432
442
  ElementReference el;
433
443
  IJSObjectReference? chart;
@@ -437,7 +447,8 @@ export async function createGroup(sampleUrl, dotnet) {
437
447
  if (!firstRender) return;
438
448
  var group = await Group.Ready;
439
449
  chart = await group.InvokeAsync<IJSObjectReference>("mount", el,
440
- new { id = Id, dir = Dir, height = Height, respondsWith = RespondsWith, geoKind = GeoKind });
450
+ new { id = Id, dir = Dir, height = Height, respondsWith = RespondsWith, geoKind = GeoKind,
451
+ lasso = Lasso ? new { rowCap = LassoRowCap } : null });
441
452
  }
442
453
 
443
454
  /// <summary>A live restyle: merged into the chart's options and repainted.</summary>
@@ -490,6 +501,9 @@ Add `@using MyCharts.Components` to `_Imports.razor`, then on a page:
490
501
  `dropLines` says which applies: `ask` carries the question, `on`/`off` means the user already
491
502
  answered (the client asked them) and the generated component applies it. Keep the answer when
492
503
  the chart is regenerated; never ask again.
504
+ - **Lasso** - when the result's `capabilities.lasso` is present, pass `Lasso="true"` (and `LassoRowCap` with its
505
+ `rowCap`): a drag around marks then selects the ones inside through the group's own selection, with no handler to
506
+ write. A chart whose result has none takes neither parameter and has no lasso.
493
507
  - **GeoKind** - the result's `geo.kind` (the text reply's `Geo:` line). Without it a map draws
494
508
  its marks over no land. `loadGeo` takes that geometry from chart-host itself, so the
495
509
  `data.geo.json` the tool also writes beside a map is the same shapes as a file: the page never
@@ -647,6 +661,11 @@ function CountryMap({ table, onPick }) { // table = the live query resu
647
661
  - **`LAYOUT_PASS` is built once, at module scope, and needs `@bicharts/chart-host` 0.6.86 or later.** After
648
662
  every render it slides a legend clear of the plot and brings text that runs past the frame back inside; the
649
663
  chart component `generate_chart` writes does the same, and a new function on each render would rebuild the host.
664
+ - **The result's `capabilities.lasso` means the chart takes chart-host's lasso (`@bicharts/chart-host` 0.6.84 or later).**
665
+ Pass `lasso: { capable: true, enabled: true }` in that chart's `options` (add `rowCap` when the result gives one), and a
666
+ drag around marks selects the ones inside through the same `onSelect` / `selects` a click uses: the gesture and the
667
+ selection are the host's, so there is no handler to write. The component `generate_chart` writes passes it already.
668
+ Without `capabilities.lasso` the chart has no lasso, and `lasso: { enabled: false }` turns it off.
650
669
 
651
670
  **Layout: a chart is never clipped or squashed.** Charts sit in cards, and a card is where they get cut off.
652
671
  - Render every chart inside `BicChartPanel`. It sizes the chart from its cell, never from the chart.