@kodart/quant-inline-chart
v0.4.0
Published
Zero-dependency inline chart renderer for Quant chat widgets: candlesticks, zones, and stacked panes on a shared time axis, plus an MCP Apps shell.
Readme
@kodart/quant-inline-chart
Zero-dependency renderer for Quant's inline chat charts. Draws candlesticks, overlay lines, column series, horizontal price zones, and vertical time zones across stacked panes that share one time axis, with a crosshair readout and automatic light/dark theming.
Built to be loaded from a CDN inside claude.ai's widget sandbox, whose CSP allows cdnjs / jsdelivr / unpkg / esm.sh only:
<script src="https://cdn.jsdelivr.net/npm/@kodart/[email protected]/quant-inline-chart.js"></script>
<div id="chart"></div>
<script>
QuantInlineChart.render(document.getElementById('chart'), payload);
</script>payload is the payload v1 contract: decimated OHLCV candles plus
bucketed point series, grouped into panes. It is produced by bt-core's
project_widget_payload (backtest-engine/crates/bt-core/src/chart/widget.rs)
and served by bt-server on POST /v1/charts/widget and
GET /v1/charts/{chart_id}/widget, surfaced to agents as the
chart_widget_payload MCP tool. See the contract documented at the top of
quant-inline-chart.js.
Earlier revisions of this file named quant-api's
POST /chart-data/{artifact_id}/widget-payloadroute and aquant.get_widget_payloadagent tool. Both were removed in the 2026-08-14 service consolidation — quant-api no longer has a chart lane at all, and the engine lane above is the only producer.
The MCP Apps shell (/app)
app.js is what an MCP App needs on top of the renderer: the host
handshake, the host's own theme and width, and — the point of that lane —
fetching the chart data itself.
import { mountQuantChartApp } from 'https://cdn.jsdelivr.net/npm/@kodart/[email protected]/app.js'
import { App, applyHostStyleVariables, applyDocumentTheme } from '<ext-apps app-with-deps.js>'
await mountQuantChartApp({
app: new App({ name: 'quant-chart', version: '0.2.0' }, {}),
root: document.getElementById('root'),
tool: 'chart_widget_payload',
args: () => toolArgs, // or a plain object
styles: { applyHostStyleVariables, applyDocumentTheme },
})Why the app fetches its own data. A tool result travels into the
conversation — the model sees its text and structuredContent. So a payload
returned by the tool that OPENED the app sits in model context, exactly as on
the show_widget lane. callServerTool is the app calling the server through
the host, and its result goes to the app alone. The opening tool therefore
returns only a small handle and the shell fetches the payload itself, which is
what lets this lane carry full resolution with nothing in context. Putting the
payload in the opening tool's result silently gives up the only advantage the
lane has.
Everything is injected, never imported: the SDK and the renderer are passed
in, so this package keeps its zero-dependency character, the host page pins the
SDK version, and the shell is testable against a fake host — see
harness-app.html (not published), which covers the happy path, host-supplied
theme and width, iframe sizing, fullscreen, and both failure paths.
The shell prefers the host's theme over matchMedia and its
containerDimensions.width over the renderer's built-in default; inside the
sandbox matchMedia describes the iframe rather than the host, and was
measured disagreeing with a dark host.
Publishing
Publish manually from this directory (npm publish — the package must stay
public, or jsdelivr/unpkg cannot serve it). Pin the exact version in any
widget <script src>; never float a tag.
Who loads this
backtest-engine/crates/bt-mcp/src/chart_app.rs — it generates the MCP App's
HTML with the CDN <script src> and the app.js import, both pinned to
RENDERER_VERSION (currently 0.2.0). That constant is the version that
actually ships; a test there asserts every origin the view loads from is
declared in the app resource's csp.resourceDomains, so publishing a new
version means bumping RENDERER_VERSION and redeploying bt-server. The
charting skill also documents the CDN URL for the show_widget lane.
Provenance
Extracted from a validated end-to-end prototype that lived in
scripts/quant-chart/widget-prototype/. That directory no longer exists —
the scripts/quant-chart CLI tooling was deleted in the 2026-08-14 service
consolidation, and its README's record of the design decisions and pipeline
test is only recoverable from git history.
