@bicharts/chart-mcp 0.6.62 → 0.6.63
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/dist/check-chart.mjs +45 -45
- package/dist/index.mjs +1 -1
- package/package.json +3 -3
package/dist/index.mjs
CHANGED
|
@@ -227,7 +227,7 @@ ${n.join(`
|
|
|
227
227
|
If these rows are a sample of a larger result, pass the whole result (every row at the chart's grain, or a csv_path): eligibility reads the row, region and category counts, so a sample can hide the chart you are looking for.`}function Ub(e,a,t,n,r,o){if(!n||t!=="name"&&t!=="namePart")return"";let i=new Set(a),s=e.filter(p=>!i.has(p)),l=_2(s,n,o5,r);if(l.tier!=="namePart"&&l.tier!=="desc"||l.rows.length===0)return"";let u=l.rows.slice(0,6).map(p=>o(p)),c=l.rows.length>6?`, and ${l.rows.length-6} more`:"",d=l.tier==="desc"?"in their descriptions":"mid-word in their names";return`${l.rows.length} more eligible type(s) match "${n}" ${d} and were left out because a name matched first: ${u.join(", ")}${c}. Re-run without name_filter to compare them.`}function al(e){let a=e.rank!==void 0?`${e.rank}. `:"\u2022 ",t=e.isPreview?" (preview)":"",n=e.language?` [${e.language}]`:"";return`${a}${e.name}${t}${n}${e.description?` \u2014 ${e.description}`:""}`}function _b(e){let a=e.filter(n=>n.recommended!==!1),t=e.filter(n=>n.recommended===!1);return t.length===0?e.map(al):["Fits this data:",...a.map(al),"","Can also render it (the chart ontology judges these a weaker fit for this data; any of them can still be requested):",...t.map(al)]}var zb=["This is RegiaBI's chart server (npm @bicharts/chart-mcp). When a request asks for RegiaBI charts, it means these tools.","Starting to build an app with charts - a Microsoft Fabric App or any other? Your FIRST step, before you scaffold anything, is get_guide (topic 'fabric-app' for a Fabric App, else 'all'): it carries the scaffold command and the setup steps.","For a Microsoft Fabric App over a semantic model, scaffold from Rayfin's dataapp template (npx -y @microsoft/create-rayfin --template dataapp), not blankapp. There, run setup_fabric_app once; query_semantic_model then runs a chart's DAX, saves every row and profiles it in ONE call.","Asked which charts suit the data (good chart types for this dataset, what could be charted)? Answer from these tools, not from general knowledge. Over a semantic model in a Fabric App, suggest_charts does it in ONE call: it queries the model's natural views and returns what fits each, with real counts. For a table you hold, list_eligible_charts.","Five rules for charts that fit on the first try:","1. Pass the chart's WHOLE result at its grain, not a sample: what fits depends on its counts. Use csv_path when long.","2. Give columns the plain names your app renders with (rename Table[Column] first), unchanged at render time.","3. Call list_eligible_charts on that data before choosing, and choose what answers the question as asked. Where a chart has controls drawn inside the chart, build no input beside it.","4. Host the code with @bicharts/chart-host. With out_dir, a D3 chart comes with chart.component.tsx beside chart.js, already wired to the binding generate_chart returns: import it rather than writing one, and never hand-build __rowIdx__ or __geo* columns.","5. Use `prompt` for what the chart shows; selection, cross-filtering, sizing and theming are the host's: filter with useBicFilter + selects, read a chart's sliders with useBicControls + controls (@bicharts/chart-host/react). Never write a select handler, toggle or clear.","The guide is also skills/bic-charts/SKILL.md in the package."].join(`
|
|
228
228
|
`);import{readFileSync as Tb}from"node:fs";import{dirname as pF,join as Rb,resolve as mF}from"node:path";import{fileURLToPath as gF}from"node:url";var Ib=["fabric-app","all","chart-shapes"];function fF(){return mF(pF(gF(import.meta.url)),"..","skills","bic-charts")}function yF(e){let t=e.replace(/^---[\s\S]*?\n---\s*\n/,"").split(/\n(?=## )/),n=new Map,r="";for(let o of t){let i=/^## (\w+)\./.exec(o);i?n.set(i[1],o.trim()):r+=o}return{preamble:r.trim(),byLabel:n}}function Eb(e="fabric-app",a=fF()){if(e==="chart-shapes")return Tb(Rb(a,"CHART-SHAPES.md"),"utf-8");let t=Tb(Rb(a,"SKILL.md"),"utf-8");if(e==="all")return t.replace(/^---[\s\S]*?\n---\s*\n/,"");let{preamble:n,byLabel:r}=yF(t),o=(r.get("3")??"").split(/\n(?=### Blazor)/)[0].trim();return[n,r.get("0"),r.get("2"),o,r.get("3a"),r.get("4")].filter(i=>!!i&&i.length>0).join(`
|
|
229
229
|
|
|
230
|
-
`)}function Qt(e,a){return cb(e,a).dir??Yt(e??process.cwd())}var NF="0.6.
|
|
230
|
+
`)}function Qt(e,a){return cb(e,a).dir??Yt(e??process.cwd())}var NF="0.6.63",Ye=new w9({name:"bic-chart",version:NF},{instructions:zb}),M5="2026-10-02T23:44:38Z src:0c023d251d50 (23 files)",xb="@bicharts/chart-host@0.6.63, @bicharts/shape-core@0.6.63",bF=I.object({columns:I.array(I.object({name:I.string(),dataType:I.enum(["String","Integer","Decimal","DateTime"]).optional().describe("Known type (DAX/semantic-model callers have it); omit to infer from values."),isMeasure:I.boolean().optional().describe("Known measure/dimension role; omit to apply the numeric-non-identifier heuristic. A semantic-model caller should set this."),format:I.string().optional().describe('Format string from the model (e.g. "$#,##0") \u2192 modelFormat.'),description:I.string().optional().describe("Column description from the model \u2192 modelDesc.")})).min(1),rows:I.array(I.union([I.array(I.any()),I.record(I.string(),I.any())])).min(1).describe("Rows as positional arrays aligned to columns, OR objects keyed by column name.")}).describe("A pre-typed {columns, rows} table (DAX query results, dataframe, arrow). Use INSTEAD of csv_path/csv_text."),tl={csv_path:I.string().optional().describe("Path to a CSV file (header row required)."),csv_text:I.string().optional().describe("Inline CSV text (alternative to csv_path)."),data:bF.optional().describe("A pre-typed {columns, rows} table \u2014 the shape a Fabric DAX query / dataframe already has. Alternative to csv_path/csv_text; when columns carry isMeasure/format/description the shape matches PBI fidelity with no guessing."),measures:I.array(I.string()).optional().describe("Columns to treat as MEASURES (the PBI 'Values' well \u2014 fields the chart aggregates). Numeric non-identifier columns are measures by default."),dimensions:I.array(I.string()).optional().describe("Columns to treat as DIMENSIONS (a PBI category/axis well \u2014 fields to group/break by). Use for numeric codes/years that should NOT be aggregated."),formats:I.record(I.string(),I.string()).optional().describe(`Per-column number/date FORMAT STRING (e.g. {"Revenue":"$#,##0","Margin":"0.0%"}). PBI supplies these from its model; a CSV can't, so provide them to steer number formatting.`),descriptions:I.record(I.string(),I.string()).optional().describe("Per-column human DESCRIPTION \u2014 gives the model semantic context the bare column name lacks."),group_by:I.array(I.string()).optional().describe("Columns that are the PRIMARY series/breakdown (the PBI 'Group by' well); the backend leads the visualization with these."),non_additive:I.array(I.string()).optional().describe("MEASURE columns that must NOT be summed (a ratio/average/rate \u2014 use mean, never stack). Usually auto-detected; this is an authoritative override."),privacy_level:I.enum(["0","10","20","30"]).optional().describe("How much detail to compute/share: 0 names only, 10 +counts, 20 +detailed stats (default), 30 +obfuscated sample rows.")};function Jt(e){return{measures:e.measures,dimensions:e.dimensions,formats:e.formats,descriptions:e.descriptions,groupBy:e.group_by,nonAdditive:e.non_additive,privacyLevel:e.privacy_level??"20"}}function U5(e){if(e.data)return Cg(e.data.columns,e.data.rows,Jt(e));if(e.csv_text)return Pt(e.csv_text,Jt(e));if(e.csv_path)return Pt(j2(Yt(e.csv_path),"utf-8"),Jt(e));throw new Error("Provide one of: data ({columns, rows}), csv_text, or csv_path.")}Ye.registerTool("assess_data_shape",{title:"Assess data shape",description:"Profile a dataset locally (no data leaves the machine): per-column types, cardinality, ranges, measure/dimension roles, temporal/ordinal detection, and cross-column signals (nesting, shared values, association). This is the same measurement engine the BIC Power BI visual uses before generating a chart.",inputSchema:tl},async e=>{let a=U5(e),t={totalRows:a.totalRows,leafCardinality:a.index.getLeafCardinality(),columns:a.shape};return{content:[{type:"text",text:JSON.stringify(t,null,2)}]}});Ye.registerTool("get_guide",{title:"Read the build guide",description:"How to build an app with these charts, from the guide this package ships. Local and free: no network, no credentials. topic 'fabric-app' (the default) is what a Microsoft Fabric App on the Rayfin data app template needs: what to pull, generating, wiring in React, the Fabric App section and the time-savers. 'all' is the whole guide, including Blazor. 'chart-shapes' lists every chart type with the columns, limits and renderers it needs. Read it before the first generate_chart call.",inputSchema:{topic:I.enum(Ib).optional().describe("'fabric-app' (default), 'all', or 'chart-shapes'.")}},async e=>{try{return{content:[{type:"text",text:Eb(e.topic??"fabric-app")}]}}catch(a){return{isError:!0,content:[{type:"text",text:`The guide could not be read from this package (${a?.message??a}). It is also published as skills/bic-charts/SKILL.md inside @bicharts/chart-mcp on npm.`}]}}});Ye.registerTool("list_eligible_charts",{title:"List eligible chart types",description:"The AUTHORITATIVE list of chart types that can render this dataset \u2014 the server's own eligibility engine, NOT a local guess. Profiles the data locally, then sends that profile to the backend. NO LLM in the loop, but this is a NETWORK call and it is METERED \u2014 see the licensing note below. By default the tool DETECTS the project's language (JS/TS vs Python from the working dir) and returns the charts for THAT language, SCOPED + RANKED by the picker's chart-type\xD7renderer weights (best first), each tagged with its source language and renderer. Pass `language` ('javascript'|'python') to force it, or `renderer` ('D3'/'PLOTLY'/\u2026) to rank one specific renderer, or renderer '' for the full renderer-agnostic eligibility across ALL renderers (also ranked, by chart type alone, with no renderer/language tag). If the language can't be auto-detected and none is given, the tool asks you to specify one. REQUIRES an active license (BIC_LICENSEE + BIC_LICENSE_KEY): eligibility IS the engine, so programmatic access to it is licensed. Free within a generous hourly allowance; beyond it a call may draw usage credits, so avoid calling it in a tight loop. The refusal names what to set.",inputSchema:{...tl,language:I.enum(["javascript","typescript","python",""]).optional().describe("Project source language \u2192 the server ranks across that language's renderers (javascript/typescript = D3/Vega, python = Plotly/matplotlib). Omit to AUTO-DETECT from the working dir; `renderer` (if given) overrides this."),project_dir:I.string().optional().describe("Directory to auto-detect the project language from (default: the folder setup_fabric_app used, else the working directory or the one project folder directly inside it)."),renderer:I.enum(["D3","PLOTLY","PYMATPLOT","VEGA",""]).optional().describe("Force a specific renderer (overrides `language`): a concrete renderer ranks that renderer's charts; '' = the full renderer-agnostic eligibility (all renderers, ranked by chart type alone). Omit to use `language`/auto-detection."),level_of_detail:I.enum(["High","Medium","Low",""]).optional().describe("How much of the data the user wants to SEE \u2014 ASK THEM, do not infer. 'High' = every observation as its own mark, 'Medium' = every row shown as a distribution, 'Low' = one mark per category. Ranks this list the same way a generation would, so what you see here is what you will get. Omit if they have expressed no preference."),name_filter:I.string().optional().describe("Show only chart types matching this text (the same matcher the Power BI and Excel chart-type lists use). Three tiers, first non-empty one wins outright: the term starting a WORD in the name ('gan' -> Gantt chart, and NOT Organization chart), then mid-word in the name ('eth' -> Choropleth), then the description. A name match therefore SUPPRESSES description matches. Case- and accent-insensitive and, by default, space- and punctuation-insensitive too ('barchart' and 'bar-chart' find Bar chart; see name_filter_match). Minimum two characters, and it never reorders - the picker's ranking is preserved and the ranks stay as issued. Applied after ranking and before `top`."),name_filter_match:I.enum(["loose","exact"]).optional().describe("How name_filter matches a name. 'loose' (the default) ignores accents, case, spaces and punctuation, so 'barchart' finds Bar chart. 'exact' folds accents and case and collapses repeated spaces, and nothing else: a name typed without its space or hyphen matches nothing. The same setting the Power BI and Excel chart-type lists offer."),top:I.number().optional().describe("Show only the top-N ranked results. Omit for all."),width:I.number().optional().describe("Viewport width px (default 800) \u2014 affects small-viewport / facet-budget gates."),height:I.number().optional().describe("Viewport height px (default 600).")}},async e=>{let a=U5(e),t="",n="",r="",o="";if(e.renderer!==void 0)t=e.renderer;else if(e.language)n=e.language;else{let f=k5(Qt(e.project_dir,C5));f.language?(n=f.language,r=` (auto-detected language: ${f.language} \u2014 ${f.reason})`):o=`Couldn't tell the project's language (${f.reason}), so this is the list across all renderers; pass language: "javascript" or "python", or project_dir, to narrow it.
|
|
231
231
|
`}Fs("list_eligible_charts",process.env);let i=await ks({shape:a.shape,totalRows:a.totalRows,leafCardinality:a.index.getLeafCardinality(),geoExtent:a.index.getGeoExtent(),width:e.width,height:e.height,renderer:t,language:n,levelOfDetail:e.level_of_detail||void 0});if(!i.ok)return{isError:!0,content:[{type:"text",text:`Could not list eligible charts: ${i.errorMessage}`}]};let s=i.charts.length>0&&i.charts[0].rank!==void 0,l=n?`${n}`:t||"all renderers",u=i.charts,c=u.length,d=yy(e.name_filter_match),p=_2(u,e.name_filter,o5,d);u=p.rows;let m;if(s?(e.top&&e.top>0&&(u=u.slice(0,e.top)),m=`Top ${u.length} chart type(s) for this data in ${l}, ranked by the picker's weights (authoritative \u2014 the server's eligibility engine; ${s5})${r}:`):m=`${u.length} chart type(s) can render this data \u2014 renderer-agnostic eligibility (authoritative \u2014 the server's eligibility engine; ${s5}):`,m=o+m,p.tier!=="all"){let f=p.tier==="desc"?" (no NAME matched; these matched on description)":p.tier==="namePart"?" (matched mid-word in the name)":"";m=`Filtered to "${e.name_filter}"${f}: ${u.length} of ${c} eligible chart type(s). The other ${c-u.length} are eligible and simply do not match the filter.
|
|
232
232
|
`+m}let h=_b(u);if(u.length===0&&p.tier!=="all"&&c>0){let f=_2(i.refused,e.name_filter,Ny,d),S=f.tier==="name"||f.tier==="namePart"?f.rows.filter(k=>k.reason).slice(0,3):[],W=S.length>0?`
|
|
233
233
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bicharts/chart-mcp",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.63",
|
|
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",
|
|
@@ -43,8 +43,8 @@
|
|
|
43
43
|
},
|
|
44
44
|
"// 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.",
|
|
45
45
|
"devDependencies": {
|
|
46
|
-
"@bicharts/chart-host": "^0.6.
|
|
47
|
-
"@bicharts/shape-core": "^0.6.
|
|
46
|
+
"@bicharts/chart-host": "^0.6.63",
|
|
47
|
+
"@bicharts/shape-core": "^0.6.63",
|
|
48
48
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
49
49
|
"@types/node": "^20.0.0",
|
|
50
50
|
"@types/papaparse": "^5.3.14",
|