@bicharts/chart-mcp 0.6.48 → 0.6.50

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.48",
3
+ "version": "0.6.50",
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.48",
51
- "@bicharts/shape-core": "^0.6.48",
50
+ "@bicharts/chart-host": "^0.6.50",
51
+ "@bicharts/shape-core": "^0.6.50",
52
52
  "@types/node": "^20.0.0",
53
53
  "@types/papaparse": "^5.3.14",
54
54
  "esbuild": "^0.28.1",
@@ -454,6 +454,94 @@ Add `@using MyCharts.Components` to `_Imports.razor`, then on a page:
454
454
  (`@media (prefers-color-scheme: dark) { ... }` on `body` or the chart's card) and the charts follow
455
455
  on the next `change` of the media query.
456
456
 
457
+ ## 3a. A Microsoft Fabric App (the Rayfin `dataapp` template)
458
+
459
+ A Fabric App built from Rayfin's `dataapp` template is a React app over a semantic model. The golden
460
+ path above applies unchanged; these are the places a data app differs, each one measured on a real
461
+ build.
462
+
463
+ **1. Pull each chart's rows at the chart's GRAIN, with a query written for that chart.** One row per
464
+ country for a country map, one per route for a flow map, one per month for a trend. The template's
465
+ writing-phase command is `npx fabric-app-data query <alias> --query '<DAX>'`. Don't pull a fact
466
+ table and aggregate it in the browser: the Execute Queries API refuses a response over 15 MB, and a
467
+ column repeated on every fact row (a country's population on each sale) sums to nonsense.
468
+
469
+ **2. Profile and generate from THE WHOLE RESULT of that query - every row, never a few pasted in.**
470
+ Eligibility reads row, region and category counts. A bivariate country map needs at least 24
471
+ countries, so the same 40-country query shown as 8 sample rows hides it, and nothing fails - you are
472
+ simply offered other charts. Save the result to a file and pass `csv_path`, or pass all of it as
473
+ `data`.
474
+
475
+ **3. Rename the query's columns to plain names first, and keep the map.** Results come back as
476
+ `DimCountry[CountryCode]` and `[Revenue per Capita]`. Generate on `CountryCode` and
477
+ `RevenuePerCapita`, and apply the SAME rename to the live rows at run time - the chart reads columns by
478
+ name. Pass the model's metadata as you rename: `isMeasure`, `format` and `description` (from
479
+ `INFO.VIEW.MEASURES()` / `INFO.VIEW.COLUMNS()`) go on each column in `data`, which is the context a
480
+ Power BI visual gets for free.
481
+
482
+ **4. Ask before choosing, then choose what answers the question.** `list_eligible_charts` on that
483
+ data. The catalogue goes past the template's own charts, and these are the ones people describe
484
+ without naming:
485
+
486
+ | the request sounds like | ask for |
487
+ | --- | --- |
488
+ | "compare A with B by country" | Bivariate World Choropleth |
489
+ | "the shipping lanes / flows between countries" | Origin-Destination Flow Map |
490
+ | "a projection I can steer with a growth rate, against our target" | What-if projection - the rate and horizon sliders are drawn inside the chart; there is no slider to build |
491
+ | "how far apart the outcomes of several growth rates land" | What-if scenarios |
492
+
493
+ If the list doesn't offer what you expected, read its notes: it names a type refused on a count
494
+ when your table is small, and it says when `name_filter` hid description matches.
495
+
496
+ **5. Render live rows through the group, with the binding the generation returned.** The template
497
+ 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:
499
+
500
+ ```tsx
501
+ import * as d3 from "d3";
502
+ import { BicChart, BicChartGroup } from "@bicharts/chart-host/react";
503
+ import code from "@/charts/country-map/chart.js?raw";
504
+ // Saved from the generate_chart result: columns = data.columns, binding = binding, geoKind = geo.kind.
505
+ import { columns, binding, geoKind } from "@/charts/country-map/meta";
506
+
507
+ // The SAME rename the chart was generated on.
508
+ const RENAME = { "DimCountry[CountryCode]": "CountryCode", "DimCountry[Country]": "Country",
509
+ "[Revenue per Capita]": "RevenuePerCapita", "[Return Rate %]": "ReturnRate" };
510
+
511
+ function CountryMap({ table, onPick }) { // table = the live query result
512
+ const rows = table.rows.map(r => Object.fromEntries(
513
+ table.columns.map((c, i) => [RENAME[c.name] ?? c.name, r[i]])));
514
+ return (
515
+ <BicChartGroup columns={columns} rows={rows}
516
+ geo={binding.geo} point={binding.point} destination={binding.destination}>
517
+ <BicChart id="map" code={code} d3={d3} geoKind={geoKind}
518
+ options={{ width, height }}
519
+ onSelect={idxs => onPick(idxs.length ? rows[idxs[0]].CountryCode : null)} />
520
+ </BicChartGroup>
521
+ );
522
+ }
523
+ ```
524
+
525
+ - **`columns`** can be the generation payload's columns as they are: the group drops any
526
+ `__rowIdx__` / `__geo*` column it is handed and builds its own from the live rows and the binding.
527
+ - **`geoKind` comes from the result's `geo.kind`**, not from `binding.geo`: a point map has a
528
+ basemap but no join, so its `binding.geo` is null.
529
+ - **`<BicChartGroup destination>` needs `@bicharts/chart-host` 0.6.50 or later.** Before it, a flow
530
+ map's far end is not built.
531
+
532
+ **6. Cross-filter across queries by KEY, not by row.** Charts fed by different queries don't share a
533
+ row space, so one group can't span them. Put each query's charts in their own group, turn a click
534
+ into the model's key (`CountryCode` above), keep it in app state, and filter the other queries with
535
+ it. Anything saved against a mark - a note, a flag - is keyed the same way, never by `__rowIdx__`
536
+ (see the row-index hazard above).
537
+
538
+ **7. A reader's choice inside a chart is view state.** A what-if chart's rate and horizon are its
539
+ own controls, reported through `viewState` on `<BicChart>` (`uiState` / `setUiState` in the
540
+ integration contract). To save a named scenario, store that - don't rebuild the slider beside it.
541
+
542
+ **8. What stays the template's job:** sign-in, the query hooks, the app's database, `VisualContainer`
543
+ headers, deployment (`npx rayfin up`). The chart is a function you render inside them.
544
+
457
545
  ## 4. Things that will otherwise cost you time
458
546
 
459
547
  - **Place names, not coordinates.** If your data has city/state/ZIP but no lat/lon, pass a