@bicharts/chart-mcp 0.6.21 → 0.6.22

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.
Files changed (2) hide show
  1. package/dist/index.mjs +1 -1
  2. package/package.json +3 -3
package/dist/index.mjs CHANGED
@@ -186,7 +186,7 @@ function showRenderFailure(e) {
186
186
  `);let n=e.code||"",r=C=>C.test(n),o=!!e.geoKind||r(/options\.geo\b|__geoIso__/),i=r(/__geoIso__/),s=n.includes(L7)||ui.some(C=>n.includes(C)),l=n.includes(W8),u=n.includes(Me),c=r(/\boptions\.onSelect\b|\bonSelect\b/),d=r(/colorScaleLow|colorScaleHigh/),h=r(/options\.aggregation\b/),m=r(/themeFg|themeAccent/),p=r(/options\.pageSize\b|llmPager/),g=r(/options\.valueAxisBaseline\b/),N=r(/options\.seasonalMarkers\b/),k=r(/__geoLat__|__geoLon__/),b=r(/__geoLatD__|__geoLonD__/),M=O7(n),U=C=>pI[C],_=Array.from(new Set(M.flatMap(C=>[...U(C)?.deps??[],C]))),P=_.map(C=>U(C)?`${C}@${U(C).version}`:C),X=_.map(C=>{let f=U(C)?.named??C.replace(/^d3-/,"").replace(/-([a-z])/g,(W,A)=>A.toUpperCase());return{pkg:C,named:f,attach:U(C)?.attach??f}}),D=M.some(C=>(U(C)?.deps?.length??0)>0),v=r(/maxMapPoints/),y=r(/geoLandColor|geoNoDataColor/),S=[`INTEGRATION CONTRACT (D3 \u2014 a portable function, merge it anywhere) [host contract v${N8}]`,""," BEFORE YOU HAND-ROLL: @bicharts/chart-host is the reference host implementation."," It does everything in this document \u2014 compiles the code, applies the option"," defaults, resolves mark clicks (including through tooltip overlays), owns the"," selection affordance, translates row indices between cross-filtered charts, and"," handles teardown. React bindings: <BicChart> / <BicChartGroup>.",gI," Everything below is the FULL HAND-ROLL SPEC \u2014 what to build if you are not using"," it, and what it is doing for you if you are.",""," Entry point: function render(container, data, options)"," \u2022 container \u2014 a DOM element to draw into (the function owns its contents)."," It MUST have a resolved width/height BEFORE you call render: the chart draws"," once at whatever size it is handed and does not re-measure. A container inside"," a display:none tab, or measured before layout, yields a 0px chart forever."," \u2022 Requires D3 v7. The code reads a bare `d3` identifier, so it needs d3 either as"," a global (`window.d3 = d3`) OR injected as a parameter by whatever compiles it"," \u2014 @bicharts/chart-host's compileRenderFn injects it, so in a bundled app there is"," NO global and you pass your imported d3 in.",' npm install d3@7 // then: import * as d3 from "d3";',...M.length?[` \u2022 D3 PLUGINS \u2014 this chart calls ${X.map(C=>C.pkg).join(", ")}.`," These are SEPARATE packages that attach onto the d3 object; the generated"," code just calls d3.<plugin>(). Install them and hand the chart ONE d3 that"," carries them. `import * as d3` is a module namespace, which is FROZEN -"," assigning a plugin onto it throws - so build a plain copy; chart-host's"," assembleD3 does exactly that:",` npm install ${P.join(" ")}`,...D?[" (install order above is also the ATTACH order \u2014 the dependency packages"," must augment d3 before the one that builds on them)"]:[],' import * as d3base from "d3";',...X.map(C=>` import { ${C.named} } from "${C.pkg}";`),` import { assembleD3 } from "${ig}";`,` const d3 = assembleD3(d3base, { ${X.map(C=>C.attach).join(", ")} });`]:[]," \u2022 It is a plain function \u2014 drop it into any app; call render() yourself.",""," data = { columns, rows } (build it in THIS exact shape from YOUR data):"," \u2022 columns: array of { name, dataType, isMeasure } in fixed order:",t," (trailing __rowIdx__ = HOST METADATA, the row's index"+(i?"; __geoIso__ = the ISO/USPS join key this tool computed, null = unmatched).":b?"; __geoLat__/__geoLon__ = the ORIGIN's coordinates and __geoLatD__/__geoLonD__ the DESTINATION's, both resolved by this tool, null = unplaceable).":k?"; __geoLat__/__geoLon__ = coordinates this tool resolved, null = unplaceable).":").")," \u2022 rows: array of POSITIONAL arrays aligned to columns:",i?" [ v0, v1, \u2026, vN, rowIndex, isoCode ] // last two = __rowIdx__, __geoIso__":b?" [ v0, \u2026, vN, rowIndex, lat, lon, prec, latD, lonD, precD ] // after __rowIdx__: origin, then destination":k?" [ v0, v1, \u2026, vN, rowIndex, lat, lon ] // last three = __rowIdx__, __geoLat__, __geoLon__":" [ v0, v1, \u2026, vN, rowIndex ] // last cell = that row's __rowIdx__"," DateTime cells are ISO-8601 strings. Missing values are null.",""," options = {"," width, height, // px"," palette: string[], // hex colors; [] = the code's own defaults"," backgroundColor, cultureCode, // '' acceptable"];return m&&S.push(" themeFg, themeAccent, // foreground / accent hex (label + play-position color)"),S.push(" allowTooltips: boolean,"),d&&S.push(" colorScaleLow, colorScaleHigh, // force the sequential scale endpoints (live restyle)"," colorScaleAutoLow, colorScaleAutoHigh, // the AUTOMATIC ends a blank one stands for; chart-host's"," // resolveOptions fills them from palette + background, so"," // a host that skips resolveOptions must set them itself"),h&&S.push(" aggregation, // '' | sum|average|median|min|max|count|distinct (live restyle)"),p&&S.push(" pageSize: number, // rows shown at a time (live restyle). 0 or absent ="," // NO PAGING: every row is drawn and a body taller"," // than the frame scrolls. This chart's rows are a"," // LIST, so a long one is normal, not an error."),g&&S.push(" valueAxisBaseline, // 'zero' | 'fit' | 'auto' (live restyle). Absent ="," // 'fit': the axis is scaled to the data, so a series"," // sitting far from zero stays legible. 'zero' forces"," // zero in; 'auto' does that only where the marks"," // measure by LENGTH. Bars keep their floor either way."),N&&S.push(" seasonalMarkers, // 'auto' | 'always' | 'never' (live restyle). Absent ="," // 'auto': the chart may name a lag with a calendar"," // period ('wk', 'annual') ONLY where it has proved"," // both halves - every gap between dates equal, AND a"," // real peak at that lag. A lag is a count of"," // OBSERVATIONS, so on a weekday-only grid a week is"," // 5, not 7. 'always' names the inferred period"," // anyway; 'never' names none."),i&&S.push(" geo: FeatureCollection, // REQUIRED \u2014 the geometry (feature.id = join code);"," // supplied by this tool (data.geo.json / preview)"," geoUnmatched: { count, examples }, // drives the 'N regions unmatched' annotation"),k&&S.push(" geo: FeatureCollection, // REQUIRED \u2014 the BASEMAP (context land, no join);"," // supplied by this tool (data.geo.json / preview)"," geoPoint: { // what the geocoding actually achieved \u2014 pass it"," precision, // 'latlon'|'zip'|'city'|'state': COARSEST tier used"," unplaced, unplacedExamples, // rows with no resolvable coordinate"," ambiguousRows }, // NOT placed: the name matches several places"," // The chart annotates these; omitting it makes a"," // sparse/approximate map LOOK exact."),b&&S.push(" geoPointDest: { \u2026 }, // the same report for the DESTINATION end (geoPoint is"," // then the origin) - pass both; the ends resolve apart."),y&&S.push(" geoLandColor, geoNoDataColor, // basemap fills ('' = the code's own defaults)"),v&&S.push(` maxMapPoints, // cap on plotted points (default ${ci}). Over it, the`," // chart keeps the LARGEST N and says so on-chart."),s&&S.push(` animMaxIdealFrames, // ${si}..${li} (default ${B7}): target keyframe count \u2014 the`," // code re-grains dates to the finest grain <= this,"," // LIVE (see 'Animation' below)",` animPlaySpeedMs, // ${ri}..${oi} (default ${F7}): ms per period`,` animLoopDelaySec, // seconds resting between auto-play passes (default ${ii});`," // 0 restarts at once, a very large value plays once"," animTimelineStyle, // '' auto | 'line' | 'boxes'"," animAutoPlay, animStopAtEnd, filtersDuringPlay, // booleans"),S.push(" uiState, setUiState, // optional persisted view-state; {} + no-op is fine"," }",""),(u||l||c)&&(S.push(" Interactivity / cross-filter (what the bridge must do \u2014 chart-host does it):"),u&&S.push(" \u2022 Every selectable mark carries a data-row-idx attribute = the comma-joined"," __rowIdx__ values it represents. On a mark click, read data-row-idx, map the"," indices back to your rows, and update your filter/state, then re-render other"," panels with the filtered rows."," \u2022 ROW INDEX HAZARD \u2014 the one thing hand-rolled integrations get wrong."," __rowIdx__ is a position WITHIN THE PAYLOAD THAT CHART RECEIVED, not an id in"," your source table. The moment you re-render a chart with filtered rows, its"," payload renumbers from zero \u2014 so the SAME integer now denotes a DIFFERENT"," record, and comparing indices across two charts silently selects the wrong"," rows. It does not throw; it just quietly filters to the wrong thing."," Keep ONE source table plus a payload-row -> source-row map per chart, and"," translate on every hop. (<BicChartGroup> exists precisely to own this.)"," \u2022 Marks are MANY-TO-MANY with rows: a legend swatch, an aggregated bar or an"," axis-group label carries every row it covers, so treat a mark as selected"," when ANY of its rows is selected \u2014 that is what makes a legend click light up"," its swatch and its bars together."," \u2022 Clicks can be SWALLOWED by the chart's own tooltip/crosshair overlay: an"," overlay painted over the marks with a transparent (not `none`) fill still"," hit-tests, so event.target is the overlay and a naive closest('[data-row-idx]')"," finds nothing and reads as 'empty canvas'. Resolve a miss through"," document.elementsFromPoint before concluding the user clicked nothing."),l&&S.push(` \u2022 This chart also DISPATCHES a bubbling \`${W8}\` CustomEvent on the`," container when its selection changes (e.g. a scrubber period). Listen for it:",` container.addEventListener('${W8}', e => {`," if (e.detail.clear) { /* selection cleared */ }",` else { const idx = e.detail.mark.getAttribute('${Me}'); /* filter */ }`," });"," \u2022 Lifecycle slots the code sets on the container (call/read them from your host):",` container.${ti}() // call to clear the chart's own selection`,` container.${ni} // a mark to apply as the initial filter (may be set)`,` container.${L7}() // CALL THIS before unmount / before re-rendering`),c?S.push(" \u2022 options.onSelect(rowIdxs:number[]) is also invoked when provided \u2014 wire it to"," your cross-filter if you prefer a callback over the DOM event."):S.push(" \u2022 NOTE: this code does NOT call options.onSelect \u2014 use data-row-idx"+(l?" + the event above.":".")),S.push("")),s&&S.push(" Animation (keyframe playback + LIVE re-grain):"," \u2022 The chart draws its own play/pause control + period scrubber. It reads the anim"," options above; sensible defaults apply if you omit them."," \u2022 DYNAMIC KEYFRAMES: the code chooses the time grain AT RENDER from"," options.animMaxIdealFrames \u2014 it truncates dates to the finest calendar grain"," (day/week/month/quarter/year/decade) whose bucket count <= that value and"," aggregates rows within each bucket. To change the granularity LIVE, just call"," render(container, data, {...options, animMaxIdealFrames: N}) again \u2014 NO new"," generation needed. (This is what the Power BI 'Max Ideal Frames' slider does.)",` \u2022 Call container.${L7}() before you re-render or unmount, to stop its timer.`,""),k&&S.push(" Geography (POINT MAP):"," \u2022 Coordinates are HOST-RESOLVED. This tool geocoded your place columns"," (city / state / zip, or lat+lon when present) and appended __geoLat__ and"," __geoLon__ to every row \u2014 the chart plots those columns directly and does NOT"," geocode anything itself. Build them the same way if you re-derive the data, or"," the map draws nothing: the code bails when it can't find the two columns."," \u2022 options.geo is the BASEMAP (grey context land), NOT a join target \u2014 there is no"," __geoIso__ and no feature.id lookup. Without it you get points on a blank field."," \u2022 Pass options.geoPoint through. It is how the chart tells the truth about"," precision (city-centroid vs exact) and about rows it could not place.",""),b&&S.push(" Geography (ROUTE - two places per row):"," \u2022 Each row is one route. This tool geocoded BOTH ends: the origin into __geoLat__/"," __geoLon__ and the destination into __geoLatD__/__geoLonD__. The chart needs both"," pairs, and refuses the data (an INVALID message, no arcs) when a row has only one."," \u2022 Pass options.geoPointDest beside options.geoPoint: an arc is only as well placed"," as its worse end, and the chart says which end was approximate.",""),i&&S.push(" Geography:"," \u2022 This is a choropleth: it REQUIRES options.geo (a GeoJSON FeatureCollection whose"," feature.id is the join code) and the __geoIso__ column in data (both provided by"," this tool \u2014 see data.geo.json when out_dir is set). Without options.geo it renders"," an empty map. Do NOT fetch or invent geometry; pass what the tool supplied.",""),S.push(" HOST PAGE PREREQUISITES (@bicharts/chart-host does all of these for you):"," \u2022 SIZE THE CONTAINER FIRST. Measure synchronously (getBoundingClientRect in a"," layout effect) and pass width/height explicitly \u2014 they have NO defaults, and a"," chart handed undefined/0 draws once at nothing and never recovers. Use a"," ResizeObserver for LATER resizes only: gating the FIRST paint on it costs"," seconds in a headless/background tab, where frames are barely produced."," \u2022 PIN TYPOGRAPHY AT THE BOUNDARY. Charts set font-size but almost never"," line-height or letter-spacing, so they inherit YOURS. A page with"," `:root { font: 18px/145% }` \u2014 the stock Vite template does exactly this \u2014 hands"," an 11px chart label a 26px line box and slices it through the middle, on a chart"," that renders perfectly elsewhere. Set `line-height: normal; letter-spacing:"," normal` on the chart container (a chart that sets its own still wins)."," \u2022 SELECTION STYLING IS YOURS. Generated code emits marks and data-row-idx; it"," never styles 'selected', because only the host knows what is selected. Add"," `lch-has-selection` to the container and `lch-mark-selected` to selected marks,"," and dim the rest via the `--lch-dim-opacity` custom property (default 0.25)."," Re-apply after EVERY render or a restyle silently drops the highlight. A chart"," that rests its marks below full opacity by design stamps `d3-lift-selected` on"," their group: paint a SELECTED mark inside that group at full opacity, or it reads"," as dimmed."," \u2022 TEAR DOWN ON UNMOUNT. Call container.__llmAnimStop__() if present before you"," re-render or unmount. Under React StrictMode effects double-mount, so without"," this every animated chart leaks a running timer per mount, on every hot reload.",""," To re-render with filtered/updated data or changed options: call"," render(container, newData, newOptions) again (it repaints the container)."),S.join(`
187
187
  `)}function lg(e,a){return a?O7(e):[]}var Oi={chartName:O.string().describe("The chart type the backend produced."),language:O.string().describe("Renderer/language of the code (D3, PYTHON, \u2026)."),version:O.number().describe("Server-side version number of this generation."),creditCost:O.number().nullable().describe("Credits billed, when the server reported them."),correlationId:O.string().describe("Ties this generation to the server-side log \u2014 quote it in any support request."),outputHash:O.string().optional().describe("The server's identity for this exact code: the same code always carries the same hash. Keep it with the chart; it names the delivered code in a support request or a later report. Absent when the server sent none."),recoveredAfterMs:O.number().optional().describe("Present when the response stream was cut after the server accepted the request and this chart was then fetched back by its correlation: milliseconds from the start of the call to delivery. creditCost is null then, because the generation was charged as usual and the fetch that delivered it added the cached-serve fee."),codeSummary:O.string().optional().describe("The backend's own one-line description of what it built."),warningMessage:O.string().optional().describe("Present when the chart drawn is NOT the chart requested: names the requested type, the reason it was overruled, and what was drawn instead. Surface it to the user verbatim \u2014 never suppress it because the chart rendered fine."),code:O.string().optional().describe("The render() source. Omitted when include_code is false."),requiredD3Plugins:O.array(O.string()).describe('D3 plugin PACKAGES this code calls (e.g. ["d3-sankey"]) \u2014 install them and pass the host ONE d3 that carries them, built with assembleD3(d3, { ...plugins }) from @bicharts/chart-host (assigning onto an ES-module d3 namespace throws), or the chart throws mid-render. Empty for core-only d3 and for non-D3 languages.'),geo:O.object({column:O.string().nullable().describe("Column joined to the geometry; null for a basemap-only point map."),kind:O.string().describe("Geometry asset kind \u2014 pass the matching geometry as options.geo."),hasGeometry:O.boolean().describe("False means no bundled geometry matched this kind: the map will draw no land."),unmatched:O.number().describe("Regions in the data with no geometry match."),unmatchedExamples:O.array(O.string())}).nullable().describe("Region/basemap wiring; null for non-geo charts."),geoPoint:O.object({placed:O.number(),total:O.number(),precision:O.string().nullable(),unplaced:O.number(),unplacedExamples:O.array(O.string()),ambiguousRows:O.number().describe("Rows whose place name matched several places and was therefore NOT placed."),ambiguousExamples:O.array(O.string())}).nullable().describe("Point-geocoding outcome (a route's ORIGIN end when geoPointDest is present). `placed` counts only rows with a coordinate: neither an unplaced nor an ambiguous name is plotted. Null when the chart geocodes no points."),geoPointDest:O.object({placed:O.number(),total:O.number(),precision:O.string().nullable(),unplaced:O.number(),unplacedExamples:O.array(O.string()),ambiguousRows:O.number().describe("Rows whose destination name matched several places and was therefore NOT placed."),ambiguousExamples:O.array(O.string())}).optional().describe("A route's DESTINATION end (an origin-destination flow map), geocoded apart from geoPoint, which is then the origin. `placed` counts only rows with a destination coordinate: neither an unplaced nor an ambiguous name is plotted. Present only for a route."),data:O.any().optional().describe("The render payload {columns, rows, \u2026} to hand render() \u2014 INCLUDING any __geoIso__/__geoLat__/__geoLon__ columns the caller cannot reconstruct. Present only when out_dir was NOT given; otherwise read files.data."),files:O.object({code:O.string().optional(),data:O.string().optional(),geo:O.string().optional(),preview:O.string().optional()}).optional().describe("Absolute paths written, when out_dir was given."),hostContract:O.string().describe("Host contract version the code targets; a host on a different MAJOR may not interoperate."),build:O.string().describe("MCP server build stamp \u2014 the bundle actually serving this call.")},A$=O.object(Oi);function ug(e){return`${e}
188
188
  export { render };
189
- `}function cg(e,a=Date.now()){if(e.isRateLimited===!0){let t=e.retryAfterSeconds,n=typeof t=="number"&&Number.isFinite(t)&&t>0?` The server accepts the next request in about ${Math.ceil(t)}s (not before ${new Date(a+Math.ceil(t)*1e3).toISOString().replace(/\.\d{3}Z$/,"Z")}).`:"";return`Rate limited (correlationId ${e.correlationId}): ${e.errorMessage||"the server is limiting requests"} The server turned this request away before generating anything; asking again at once gets the same answer.`+n}return`Generation failed (correlationId ${e.correlationId}): ${e.errorMessage||"no code returned"}`}function Di(e,a){return e-(a?.unplaced??0)-(a?.ambiguousRows??0)}function $i(e,a,t,n){let r=[a.city&&"city",a.state&&"state",a.zip&&"zip",a.lat&&a.lon&&"lat/lon"].filter(Boolean).join("+");return`${e}: geocoded ${Di(n,t)}/${n} from ${r}`+(t?.precision?` at ${t.precision} precision`:"")+(t&&t.unplaced>0?` \u2014 ${t.unplaced} unplaced: ${t.unplacedExamples.join(", ")}`:"")+(t&&t.ambiguousRows>0?` \u2014 ${t.ambiguousRows} not placed, the name matches several places: ${(t.ambiguousExamples??[]).join(", ")}`:"")}function dg(e){let a=e.renderData.geoPoint,t=e.renderData.geoPointDest,n=e.renderData.geoUnmatched;return{chartName:e.res.finalChartName||"",language:e.res.language,version:e.res.version,creditCost:e.res.creditCost??null,correlationId:e.res.correlationId,...e.res.outputHash?{outputHash:e.res.outputHash}:{},...e.res.recoveredAfterMs!=null?{recoveredAfterMs:e.res.recoveredAfterMs}:{},...e.res.codeSummary?{codeSummary:e.res.codeSummary}:{},...e.res.warningMessage?{warningMessage:e.res.warningMessage}:{},...e.withCode?{code:e.code}:{},requiredD3Plugins:e.plugins,geo:e.res.geoKind?{column:e.res.geoColumn||null,kind:e.res.geoKind,hasGeometry:e.hasGeometry,unmatched:n?.count??0,unmatchedExamples:n?.examples??[]}:null,geoPoint:a?{placed:Di(e.renderData.rows.length,a),total:e.renderData.rows.length,precision:a.precision??null,unplaced:a.unplaced??0,unplacedExamples:a.unplacedExamples??[],ambiguousRows:a.ambiguousRows??0,ambiguousExamples:a.ambiguousExamples??[]}:null,...t?{geoPointDest:{placed:Di(e.renderData.rows.length,t),total:e.renderData.rows.length,precision:t.precision??null,unplaced:t.unplaced??0,unplacedExamples:t.unplacedExamples??[],ambiguousRows:t.ambiguousRows??0,ambiguousExamples:t.ambiguousExamples??[]}}:{},...e.files?{files:e.files}:{data:e.renderData},hostContract:e.hostContract,build:e.build}}hg();mg();yg();/*! @bicharts/chart-host — Apache-2.0. Bundled reference data: GeoNames (https://www.geonames.org/) CC BY 4.0; Natural Earth and US Census/TIGER (public domain). Full text: NOTICE in this package. */function fg(e){if(!e)return;let a=e.toLowerCase();if(/^country/.test(a))return e5;if(a==="us-state-code"||a==="us-state-name")return gg;if(a==="us-zip5")return pg;if(a==="north-america")return fI();if(a==="world")return yI()}var Xi;function yI(){return Xi||(Xi={type:"FeatureCollection",features:e5.features.filter(e=>e?.geometry&&e.geometry.type!=="Point")}),Xi}var Hi;function fI(){return Hi||(Hi={type:"FeatureCollection",features:e5.features.filter(e=>Kp.has(e?.id)&&e?.geometry&&e.geometry.type!=="Point").map(e=>Vp(e,Zp)).filter(e=>e!==null)}),Hi}import{readdirSync as NI,existsSync as WI,statSync as Ng}from"node:fs";import{join as Gi}from"node:path";var SI=["package.json","tsconfig.json","jsconfig.json","deno.json","deno.jsonc","angular.json","svelte.config.js","vite.config.ts","vite.config.js","pnpm-lock.yaml","yarn.lock","package-lock.json"],bI=["pyproject.toml","requirements.txt","setup.py","setup.cfg","Pipfile","poetry.lock","environment.yml","environment.yaml","conda.yaml","tox.ini"],kI=new Set([".js",".jsx",".mjs",".cjs",".ts",".tsx"]),vI=new Set([".py",".pyi"]);function Wg(e,a){return a.some(t=>WI(Gi(e,t)))}function CI(e){let a=0,t=0,n=o=>{let i;try{i=NI(o)}catch{return}for(let s of i){if(s==="node_modules"||s===".git"||s.startsWith("."))continue;let l=Gi(o,s),u=!1;try{u=Ng(l).isFile()}catch{continue}if(!u)continue;let c=s.lastIndexOf(".");if(c<0)continue;let d=s.slice(c).toLowerCase();kI.has(d)?a++:vI.has(d)&&t++}};n(e);let r=Gi(e,"src");try{Ng(r).isDirectory()&&n(r)}catch{}return{js:a,py:t}}function Ki(e=process.cwd()){let a=Wg(e,SI),t=Wg(e,bI);if(a&&!t)return{language:"javascript",reason:`found a JS/TS project marker in ${e}`};if(t&&!a)return{language:"python",reason:`found a Python project marker in ${e}`};let{js:n,py:r}=CI(e);return n>0&&r===0?{language:"javascript",reason:`found ${n} JS/TS source file(s) and no Python in ${e}`}:r>0&&n===0?{language:"python",reason:`found ${r} Python source file(s) and no JS/TS in ${e}`}:n>0&&r>0?n>=r*3?{language:"javascript",reason:`JS/TS files dominate (${n} vs ${r} Python) in ${e}`}:r>=n*3?{language:"python",reason:`Python files dominate (${r} vs ${n} JS/TS) in ${e}`}:{language:null,reason:`mixed JS/TS (${n}) and Python (${r}) in ${e}`}:a&&t?{language:null,reason:`both JS and Python project markers present in ${e}`}:{language:null,reason:`no JS or Python markers or source files found in ${e}`}}var AI="0.6.21",n5=new Ia({name:"bic-chart",version:AI}),Vi="2026-09-25T04:46:12Z src:d248b3bdfaf3 (8 files)",UI=O.object({columns:O.array(O.object({name:O.string(),dataType:O.enum(["String","Integer","Decimal","DateTime"]).optional().describe("Known type (DAX/semantic-model callers have it); omit to infer from values."),isMeasure:O.boolean().optional().describe("Known measure/dimension role; omit to apply the numeric-non-identifier heuristic. A semantic-model caller should set this."),format:O.string().optional().describe('Format string from the model (e.g. "$#,##0") \u2192 modelFormat.'),description:O.string().optional().describe("Column description from the model \u2192 modelDesc.")})).min(1),rows:O.array(O.union([O.array(O.any()),O.record(O.string(),O.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."),Ji={csv_path:O.string().optional().describe("Path to a CSV file (header row required)."),csv_text:O.string().optional().describe("Inline CSV text (alternative to csv_path)."),data:UI.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:O.array(O.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:O.array(O.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:O.record(O.string(),O.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:O.record(O.string(),O.string()).optional().describe("Per-column human DESCRIPTION \u2014 gives the model semantic context the bare column name lacks."),group_by:O.array(O.string()).optional().describe("Columns that are the PRIMARY series/breakdown (the PBI 'Group by' well); the backend leads the visualization with these."),non_additive:O.array(O.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:O.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 Zi(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 qi(e){if(e.data)return Qh(e.data.columns,e.data.rows,Zi(e));if(e.csv_text)return No(e.csv_text,Zi(e));if(e.csv_path)return No(MI(Sg(e.csv_path),"utf-8"),Zi(e));throw new Error("Provide one of: data ({columns, rows}), csv_text, or csv_path.")}n5.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:Ji},async e=>{let a=qi(e),t={totalRows:a.totalRows,leafCardinality:a.index.getLeafCardinality(),columns:a.shape};return{content:[{type:"text",text:JSON.stringify(t,null,2)}]}});n5.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 (ChartFilter.GetCharts), 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:{...Ji,language:O.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:O.string().optional().describe("Directory to auto-detect the project language from (default: the MCP server's working directory)."),renderer:O.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:O.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:O.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, 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`."),top:O.number().optional().describe("Show only the top-N ranked results. Omit for all."),width:O.number().optional().describe("Viewport width px (default 800) \u2014 affects small-viewport / facet-budget gates."),height:O.number().optional().describe("Viewport height px (default 600).")}},async e=>{let a=qi(e),t="",n="",r="";if(e.renderer!==void 0)t=e.renderer;else if(e.language)n=e.language;else{let m=Ki(e.project_dir);if(m.language)n=m.language,r=` (auto-detected language: ${m.language} \u2014 ${m.reason})`;else return{content:[{type:"text",text:`Couldn't determine the project language: ${m.reason}. Re-run with language: "javascript" or "python" (or renderer: "D3"/"PLOTLY"/\u2026 for a specific renderer, or renderer: "" for the full renderer-agnostic list).`}]}}let o=await ng({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(!o.ok)return{isError:!0,content:[{type:"text",text:`Could not list eligible charts: ${o.errorMessage}`}]};let i=o.charts.length>0&&o.charts[0].rank!==void 0,s=n?`${n}`:t||"all renderers",l=o.charts,u=l.length,c=Ci(l,e.name_filter,Sm);l=c.rows;let d;if(i?(e.top&&e.top>0&&(l=l.slice(0,e.top)),d=`Top ${l.length} chart type(s) for this data in ${s}, ranked by the picker's weights (authoritative \u2014 server ChartFilter + ScoreChartTypes, no credits)${r}:`):d=`${l.length} chart type(s) can render this data \u2014 renderer-agnostic eligibility (authoritative \u2014 ChartFilter.GetCharts, no credits):`,c.tier!=="all"){let m=c.tier==="desc"?" (no NAME matched; these matched on description)":c.tier==="namePart"?" (matched mid-word in the name)":"";d=`Filtered to "${e.name_filter}"${m}: ${l.length} of ${u} eligible chart type(s). The other ${u-l.length} are eligible and simply do not match the filter.
189
+ `}function cg(e,a=Date.now()){if(e.isRateLimited===!0){let t=e.retryAfterSeconds,n=typeof t=="number"&&Number.isFinite(t)&&t>0?` The server accepts the next request in about ${Math.ceil(t)}s (not before ${new Date(a+Math.ceil(t)*1e3).toISOString().replace(/\.\d{3}Z$/,"Z")}).`:"";return`Rate limited (correlationId ${e.correlationId}): ${e.errorMessage||"the server is limiting requests"} The server turned this request away before generating anything; asking again at once gets the same answer.`+n}return`Generation failed (correlationId ${e.correlationId}): ${e.errorMessage||"no code returned"}`}function Di(e,a){return e-(a?.unplaced??0)-(a?.ambiguousRows??0)}function $i(e,a,t,n){let r=[a.city&&"city",a.state&&"state",a.zip&&"zip",a.lat&&a.lon&&"lat/lon"].filter(Boolean).join("+");return`${e}: geocoded ${Di(n,t)}/${n} from ${r}`+(t?.precision?` at ${t.precision} precision`:"")+(t&&t.unplaced>0?` \u2014 ${t.unplaced} unplaced: ${t.unplacedExamples.join(", ")}`:"")+(t&&t.ambiguousRows>0?` \u2014 ${t.ambiguousRows} not placed, the name matches several places: ${(t.ambiguousExamples??[]).join(", ")}`:"")}function dg(e){let a=e.renderData.geoPoint,t=e.renderData.geoPointDest,n=e.renderData.geoUnmatched;return{chartName:e.res.finalChartName||"",language:e.res.language,version:e.res.version,creditCost:e.res.creditCost??null,correlationId:e.res.correlationId,...e.res.outputHash?{outputHash:e.res.outputHash}:{},...e.res.recoveredAfterMs!=null?{recoveredAfterMs:e.res.recoveredAfterMs}:{},...e.res.codeSummary?{codeSummary:e.res.codeSummary}:{},...e.res.warningMessage?{warningMessage:e.res.warningMessage}:{},...e.withCode?{code:e.code}:{},requiredD3Plugins:e.plugins,geo:e.res.geoKind?{column:e.res.geoColumn||null,kind:e.res.geoKind,hasGeometry:e.hasGeometry,unmatched:n?.count??0,unmatchedExamples:n?.examples??[]}:null,geoPoint:a?{placed:Di(e.renderData.rows.length,a),total:e.renderData.rows.length,precision:a.precision??null,unplaced:a.unplaced??0,unplacedExamples:a.unplacedExamples??[],ambiguousRows:a.ambiguousRows??0,ambiguousExamples:a.ambiguousExamples??[]}:null,...t?{geoPointDest:{placed:Di(e.renderData.rows.length,t),total:e.renderData.rows.length,precision:t.precision??null,unplaced:t.unplaced??0,unplacedExamples:t.unplacedExamples??[],ambiguousRows:t.ambiguousRows??0,ambiguousExamples:t.ambiguousExamples??[]}}:{},...e.files?{files:e.files}:{data:e.renderData},hostContract:e.hostContract,build:e.build}}hg();mg();yg();/*! @bicharts/chart-host — Apache-2.0. Bundled reference data: GeoNames (https://www.geonames.org/) CC BY 4.0; Natural Earth and US Census/TIGER (public domain). Full text: NOTICE in this package. */function fg(e){if(!e)return;let a=e.toLowerCase();if(/^country/.test(a))return e5;if(a==="us-state-code"||a==="us-state-name")return gg;if(a==="us-zip5")return pg;if(a==="north-america")return fI();if(a==="world")return yI()}var Xi;function yI(){return Xi||(Xi={type:"FeatureCollection",features:e5.features.filter(e=>e?.geometry&&e.geometry.type!=="Point")}),Xi}var Hi;function fI(){return Hi||(Hi={type:"FeatureCollection",features:e5.features.filter(e=>Kp.has(e?.id)&&e?.geometry&&e.geometry.type!=="Point").map(e=>Vp(e,Zp)).filter(e=>e!==null)}),Hi}import{readdirSync as NI,existsSync as WI,statSync as Ng}from"node:fs";import{join as Gi}from"node:path";var SI=["package.json","tsconfig.json","jsconfig.json","deno.json","deno.jsonc","angular.json","svelte.config.js","vite.config.ts","vite.config.js","pnpm-lock.yaml","yarn.lock","package-lock.json"],bI=["pyproject.toml","requirements.txt","setup.py","setup.cfg","Pipfile","poetry.lock","environment.yml","environment.yaml","conda.yaml","tox.ini"],kI=new Set([".js",".jsx",".mjs",".cjs",".ts",".tsx"]),vI=new Set([".py",".pyi"]);function Wg(e,a){return a.some(t=>WI(Gi(e,t)))}function CI(e){let a=0,t=0,n=o=>{let i;try{i=NI(o)}catch{return}for(let s of i){if(s==="node_modules"||s===".git"||s.startsWith("."))continue;let l=Gi(o,s),u=!1;try{u=Ng(l).isFile()}catch{continue}if(!u)continue;let c=s.lastIndexOf(".");if(c<0)continue;let d=s.slice(c).toLowerCase();kI.has(d)?a++:vI.has(d)&&t++}};n(e);let r=Gi(e,"src");try{Ng(r).isDirectory()&&n(r)}catch{}return{js:a,py:t}}function Ki(e=process.cwd()){let a=Wg(e,SI),t=Wg(e,bI);if(a&&!t)return{language:"javascript",reason:`found a JS/TS project marker in ${e}`};if(t&&!a)return{language:"python",reason:`found a Python project marker in ${e}`};let{js:n,py:r}=CI(e);return n>0&&r===0?{language:"javascript",reason:`found ${n} JS/TS source file(s) and no Python in ${e}`}:r>0&&n===0?{language:"python",reason:`found ${r} Python source file(s) and no JS/TS in ${e}`}:n>0&&r>0?n>=r*3?{language:"javascript",reason:`JS/TS files dominate (${n} vs ${r} Python) in ${e}`}:r>=n*3?{language:"python",reason:`Python files dominate (${r} vs ${n} JS/TS) in ${e}`}:{language:null,reason:`mixed JS/TS (${n}) and Python (${r}) in ${e}`}:a&&t?{language:null,reason:`both JS and Python project markers present in ${e}`}:{language:null,reason:`no JS or Python markers or source files found in ${e}`}}var AI="0.6.22",n5=new Ia({name:"bic-chart",version:AI}),Vi="2026-09-25T04:54:43Z src:0d36c84549d8 (8 files)",UI=O.object({columns:O.array(O.object({name:O.string(),dataType:O.enum(["String","Integer","Decimal","DateTime"]).optional().describe("Known type (DAX/semantic-model callers have it); omit to infer from values."),isMeasure:O.boolean().optional().describe("Known measure/dimension role; omit to apply the numeric-non-identifier heuristic. A semantic-model caller should set this."),format:O.string().optional().describe('Format string from the model (e.g. "$#,##0") \u2192 modelFormat.'),description:O.string().optional().describe("Column description from the model \u2192 modelDesc.")})).min(1),rows:O.array(O.union([O.array(O.any()),O.record(O.string(),O.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."),Ji={csv_path:O.string().optional().describe("Path to a CSV file (header row required)."),csv_text:O.string().optional().describe("Inline CSV text (alternative to csv_path)."),data:UI.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:O.array(O.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:O.array(O.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:O.record(O.string(),O.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:O.record(O.string(),O.string()).optional().describe("Per-column human DESCRIPTION \u2014 gives the model semantic context the bare column name lacks."),group_by:O.array(O.string()).optional().describe("Columns that are the PRIMARY series/breakdown (the PBI 'Group by' well); the backend leads the visualization with these."),non_additive:O.array(O.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:O.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 Zi(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 qi(e){if(e.data)return Qh(e.data.columns,e.data.rows,Zi(e));if(e.csv_text)return No(e.csv_text,Zi(e));if(e.csv_path)return No(MI(Sg(e.csv_path),"utf-8"),Zi(e));throw new Error("Provide one of: data ({columns, rows}), csv_text, or csv_path.")}n5.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:Ji},async e=>{let a=qi(e),t={totalRows:a.totalRows,leafCardinality:a.index.getLeafCardinality(),columns:a.shape};return{content:[{type:"text",text:JSON.stringify(t,null,2)}]}});n5.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 (ChartFilter.GetCharts), 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:{...Ji,language:O.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:O.string().optional().describe("Directory to auto-detect the project language from (default: the MCP server's working directory)."),renderer:O.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:O.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:O.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, 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`."),top:O.number().optional().describe("Show only the top-N ranked results. Omit for all."),width:O.number().optional().describe("Viewport width px (default 800) \u2014 affects small-viewport / facet-budget gates."),height:O.number().optional().describe("Viewport height px (default 600).")}},async e=>{let a=qi(e),t="",n="",r="";if(e.renderer!==void 0)t=e.renderer;else if(e.language)n=e.language;else{let m=Ki(e.project_dir);if(m.language)n=m.language,r=` (auto-detected language: ${m.language} \u2014 ${m.reason})`;else return{content:[{type:"text",text:`Couldn't determine the project language: ${m.reason}. Re-run with language: "javascript" or "python" (or renderer: "D3"/"PLOTLY"/\u2026 for a specific renderer, or renderer: "" for the full renderer-agnostic list).`}]}}let o=await ng({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(!o.ok)return{isError:!0,content:[{type:"text",text:`Could not list eligible charts: ${o.errorMessage}`}]};let i=o.charts.length>0&&o.charts[0].rank!==void 0,s=n?`${n}`:t||"all renderers",l=o.charts,u=l.length,c=Ci(l,e.name_filter,Sm);l=c.rows;let d;if(i?(e.top&&e.top>0&&(l=l.slice(0,e.top)),d=`Top ${l.length} chart type(s) for this data in ${s}, ranked by the picker's weights (authoritative \u2014 server ChartFilter + ScoreChartTypes, no credits)${r}:`):d=`${l.length} chart type(s) can render this data \u2014 renderer-agnostic eligibility (authoritative \u2014 ChartFilter.GetCharts, no credits):`,c.tier!=="all"){let m=c.tier==="desc"?" (no NAME matched; these matched on description)":c.tier==="namePart"?" (matched mid-word in the name)":"";d=`Filtered to "${e.name_filter}"${m}: ${l.length} of ${u} eligible chart type(s). The other ${u-l.length} are eligible and simply do not match the filter.
190
190
  `+d}let h=l.map(m=>{if(m.rank!==void 0){let p=m.language?` [${m.language}]`:"",g=m.score!==void 0?` (${m.score})`:"";return`${m.rank}. ${m.name}${p}${g}${m.description?` \u2014 ${m.description}`:""}`}return`\u2022 ${m.name}${m.description?` \u2014 ${m.description}`:""}`});if(l.length===0&&c.tier!=="all"&&u>0){let m=Ci(o.refused,e.name_filter,bm),p=m.tier==="name"||m.tier==="namePart"?m.rows.filter(N=>N.reason).slice(0,3):[],g=p.length>0?`
191
191
 
192
192
  It IS in the catalogue and was checked, and here is why it was refused for this data:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bicharts/chart-mcp",
3
- "version": "0.6.21",
3
+ "version": "0.6.22",
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",
@@ -46,8 +46,8 @@
46
46
  "zod": "^3.23.0"
47
47
  },
48
48
  "devDependencies": {
49
- "@bicharts/chart-host": "^0.6.21",
50
- "@bicharts/shape-core": "^0.6.21",
49
+ "@bicharts/chart-host": "^0.6.22",
50
+ "@bicharts/shape-core": "^0.6.22",
51
51
  "@types/node": "^20.0.0",
52
52
  "@types/papaparse": "^5.3.14",
53
53
  "esbuild": "^0.28.1",