@bicharts/chart-mcp 0.6.49 → 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/LICENSE.txt +1 -1
- package/dist/index.mjs +47 -42
- package/package.json +3 -3
- package/skills/bic-charts/SKILL.md +88 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bicharts/chart-mcp",
|
|
3
|
-
"version": "0.6.
|
|
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.
|
|
51
|
-
"@bicharts/shape-core": "^0.6.
|
|
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
|