@bicharts/chart-mcp 0.6.51 → 0.6.52

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.51",
3
+ "version": "0.6.52",
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). Requires a BIC trial or paid account.",
5
5
  "keywords": [
6
6
  "mcp",
@@ -47,8 +47,8 @@
47
47
  "zod": "^3.23.0"
48
48
  },
49
49
  "devDependencies": {
50
- "@bicharts/chart-host": "^0.6.51",
51
- "@bicharts/shape-core": "^0.6.51",
50
+ "@bicharts/chart-host": "^0.6.52",
51
+ "@bicharts/shape-core": "^0.6.52",
52
52
  "@types/node": "^20.0.0",
53
53
  "@types/papaparse": "^5.3.14",
54
54
  "esbuild": "^0.28.1",
@@ -460,6 +460,13 @@ A Fabric App built from Rayfin's `dataapp` template is a React app over a semant
460
460
  path above applies unchanged; these are the places a data app differs, each one measured on a real
461
461
  build.
462
462
 
463
+ **0. Once, right after scaffolding: `setup_fabric_app`.** It installs `@bicharts/chart-host` and `d3`, adds
464
+ `src/lib/bic-chart-options.ts` (`useBicChartOptions()`: the app's Fabric theme - palette, foreground,
465
+ background - as chart options, so charts follow light and dark) and `src/components/bic-chart-panel.tsx`
466
+ (`<BicChartPanel result={useSemanticModelQuery(...)}>`: a box that measures itself and shows the query's
467
+ loading, error and empty states), and ignores `.bic/`. It never overwrites a file, and its reply ends with a
468
+ worked example.
469
+
463
470
  **1. Pull each chart's rows at the chart's GRAIN, with a query written for that chart.** One row per
464
471
  country for a country map, one per route for a flow map, one per month for a trend. The template's
465
472
  writing-phase command is `npx fabric-app-data query <alias> --query '<DAX>'`. Don't pull a fact
@@ -479,6 +486,15 @@ name. Pass the model's metadata as you rename: `isMeasure`, `format` and `descri
479
486
  `INFO.VIEW.MEASURES()` / `INFO.VIEW.COLUMNS()`) go on each column in `data`, which is the context a
480
487
  Power BI visual gets for free.
481
488
 
489
+ **Steps 1-3 are one call: `query_semantic_model`.** Give it the chart's DAX (`dax`, or a `.dax` file) and a
490
+ `name`; it runs the query through the project's own `fabric-app-data` CLI, renames the headers (dropping the
491
+ table and any symbols: `[Return Rate %]` becomes `ReturnRate`), saves every row to `.bic/data/<name>.csv`,
492
+ reads each column's role, format and description from the model (`INFO.VIEW.MEASURES()` / `INFO.VIEW.COLUMNS()`),
493
+ and profiles the result. Its reply ends with the arguments for the next two calls - `csv_path`, `measures`,
494
+ `dimensions`, `formats`, `descriptions` - so pass them to `list_eligible_charts` and `generate_chart` as they
495
+ are. The CLI returns at most 1,000 rows; the tool says so when it cut a result, and the fix is a query at the
496
+ chart's grain.
497
+
482
498
  **4. Ask before choosing, then choose what answers the question.** `list_eligible_charts` on that
483
499
  data. The catalogue goes past the template's own charts, and these are the ones people describe
484
500
  without naming:
@@ -495,7 +511,30 @@ when your table is small, and it says when `name_filter` hid description matches
495
511
 
496
512
  **5. Render live rows through the group, with the binding the generation returned.** The template
497
513
  queries at run time (`useSemanticModelQuery`), so the payload is rebuilt from live rows every time -
498
- never hand-built. `generate_chart` returns `binding` (`geo`, `point`, `destination`); pass all three:
514
+ never hand-built. **With `out_dir`, a D3 chart arrives with `chart.component.tsx` beside `chart.js`: a component that
515
+ already does all of this** - the columns, the binding (`geo`, `point`, `destination`), the geometry kind, the
516
+ d3 plugins the chart calls, and `rowsFromTable`, which maps a DAX result's `Table[Column]` and `[Measure]`
517
+ headers onto the chart's plain names. Use it as it is:
518
+
519
+ ```tsx
520
+ // The component is named after the chart type ("Bivariate World Choropleth" -> BivariateWorldChoroplethChart).
521
+ import { BivariateWorldChoroplethChart, rowsFromTable } from "@/charts/country-map/chart.component";
522
+
523
+ import { BicChartPanel } from "@/components/bic-chart-panel";
524
+ import { useBicChartOptions } from "@/lib/bic-chart-options";
525
+
526
+ const result = useSemanticModelQuery({ connection: "<alias>", query: QUERY }); // the DAX it was generated on
527
+ const options = useBicChartOptions();
528
+ <BicChartPanel result={result}>
529
+ {(table, size) => ( // table = the live { columns, rows } result
530
+ <BivariateWorldChoroplethChart table={table} {...size} options={options}
531
+ onSelectRows={picked => onPick(picked[0]?.CountryCode ?? null)} />
532
+ )}
533
+ </BicChartPanel>
534
+ ```
535
+
536
+ It is rewritten with the chart on every generation, so keep app logic outside it. What it does, if you
537
+ host by hand instead - `generate_chart` returns `binding`; pass all three:
499
538
 
500
539
  ```tsx
501
540
  import * as d3 from "d3";
@@ -535,6 +574,28 @@ into the model's key (`CountryCode` above), keep it in app state, and filter the
535
574
  it. Anything saved against a mark - a note, a flag - is keyed the same way, never by `__rowIdx__`
536
575
  (see the row-index hazard above).
537
576
 
577
+ **Notes (or flags) shown ON a mark: pass `annotations`, never measure the chart.** The host draws the
578
+ badge on the key's mark after every render and keeps it there through filters, re-queries and zooms;
579
+ it never reads a row position. From a notes table keyed by the model's key:
580
+
581
+ ```tsx
582
+ <BivariateWorldChoroplethChart table={table} {...size} options={options}
583
+ annotations={notes.map(n => ({ column: "CountryCode", value: n.countryCode,
584
+ label: String(n.count), title: n.latestText }))}
585
+ onAnnotationClick={a => openNotes(a.value)} />
586
+ ```
587
+
588
+ A route is keyed by BOTH of its ends with `where` - no key column to add, no query to change and no
589
+ chart to regenerate:
590
+
591
+ ```tsx
592
+ annotations={laneNotes.map(n => ({ where: { OriginCountryCode: n.origin, DestinationCountryCode: n.dest },
593
+ label: String(n.count), title: n.latestText }))}
594
+ ```
595
+
596
+ Don't read `data-row-idx` or bounding boxes to place anything: that breaks on the next redraw, and
597
+ this does it for you.
598
+
538
599
  **7. A reader's choice inside a chart is view state.** A what-if chart's rate and horizon are its
539
600
  own controls, reported through `viewState` on `<BicChart>` (`uiState` / `setUiState` in the
540
601
  integration contract). To save a named scenario, store that - don't rebuild the slider beside it.