Getting Started
Add interactive, agent-ready charts, dashboards, and views to your MCP server with @bonnard/mcp-charts.
@bonnard/mcp-charts adds interactive charts to any MCP server. Your agent asks for data, you run
the query, and the result renders as an interactive chart inside the MCP host (Claude, ChatGPT, and
other MCP Apps clients). You write no frontend code: one widget renders across every host.
Install
npm install @bonnard/mcp-chartsTwo ways to add charts
There are two shapes, depending on who chooses the query:
- Ad-hoc
visualize(the agent writes SQL). Add a genericvisualizetool withaddCharts. The agent writes SQL against your warehouse and the rows render as a chart. Best for open-ended exploration. - Named views (you author the queries). Register named views with
addViews. Each view returns a single chart or a full dashboard, and the agent picks a view instead of writing SQL. Best for repeatable analytics.
Both paths render through the same ui://bonnard/chart widget and the same encoding logic. Start with
visualize below; jump to Named views for the authored path (and
Dashboards for the DashboardSpec a dashboard view returns).
Quickstart: the visualize tool
Call addCharts on your existing MCP server and give it a read-only query callback. Use a bundled
warehouse adapter so the driver's column types become chart field kinds:
import { addCharts } from "@bonnard/mcp-charts";
import { postgresRunSql } from "@bonnard/mcp-charts/postgres";
addCharts(server, {
runSql: postgresRunSql(pool), // maps driver column types to chart field kinds
discovery: { toolName: "explore_schema" }, // your schema-discovery tool
});That registers a visualize tool and a ui://bonnard/chart widget resource. The agent calls
visualize with SQL (and optional presentation hints); the rows render as a chart in the host, with
a text fallback for non-widget clients. The agent chooses the chart type, or omit it to auto-detect
from the shape of the data. See Chart Types.
Warehouse adapters
Adapters ship for Postgres, BigQuery, DuckDB, Snowflake, and Databricks. Each lives at its own import subpath and turns a native driver result into typed chart data. See Warehouse Adapters.
Writing runSql by hand
Without an adapter, return ChartData yourself. Types matter: the resolver infers field kinds from
the row values, so a driver that returns numbers as strings (Postgres NUMERIC/BIGINT, for
example) charts a measure as a category unless you declare typed fields. This "numbers must be
numbers" story, and how to declare types, is covered in
Connecting a Database.
addCharts(server, {
runSql: async (sql) => ({ rows: await db.query(sql) }), // only if your driver returns native JS types
});The full export surface
Beyond addCharts, the package exports the authoring surface for dashboards and views:
| Export | What it does |
|---|---|
addCharts | Register the ad-hoc visualize tool (agent writes SQL). |
addViews | Register explore_views + render_view over a set of named views. |
chart | Build a ChartSpec from rows or typed ChartData. |
chartCell | Build a dashboard chart cell from rows or typed ChartData. |
explain | Diagnose the encoding in a test, without rendering. |
buildChartData, defaultNormalizeCell, assertReadOnlySql | Adapter authoring kit. See API Reference. |
Security
visualize executes agent-written SQL against your database. Treat it as untrusted input. Connect
runSql to a read-only, least-privilege role scoped to the data you want exposed. Your database
permissions are the only security boundary; the SDK does not sandbox or restrict queries.