smart-charts-mcp
# smart-charts-mcp
Turn CSV/TSV/TXT/JSON data into standalone interactive ECharts HTML charts —
32 chart types, 3 themes, fully offline (ECharts JS is bundled, **zero CDN**,
works behind corporate firewalls and in air-gapped environments).
Ships as an MCP server so any MCP-capable agent (Claude, Cursor, VS Code,
WorkBuddy, ...) can profile a dataset and render charts as a tool call,
getting the HTML artifact plus rendering statistics back in the same response.
## Why this exists — the traps it removes
| Without it you will hit... | This server handles it |
|---|---|
| Charts that silently use a CDN and render blank offline | ECharts is inlined into every HTML file |
| Agent guessing chart types and columns from vibes | `profile_data` returns objective facts (dtype/cardinality/missingness/signals) first; the agent decides from evidence |
| Captions citing numbers that are not in the chart | `render_chart` returns `plot_stats` + `data_preview`; those are the only numbers a caption may quote |
| Aggregating by data row instead of by entity (double counting) | `source_rows` / `plotted_rows` / `unique_entities` audit fields on every render |
| LLM-written pandas snippets doing unsafe things | `transform_code` runs in a sandbox (AST whitelist + blacklisted builtins, no file/network I/O) |
| Misleading gauge/liquid "achievement" numbers | `achievement` is only computed when you pass an explicit business `target` |
## Tools
- `doctor()` — dependency + asset preflight.
- `profile_data(data_text, filename="data.csv")` — dataset profile (read-only, nothing rendered).
- `render_chart(data_text, chart_type, title=..., x_axis=..., y_axis=..., transform_code=..., annotation=..., theme=..., target=..., ...)` → `{ok, html, chart}`.
- `list_chart_types()` — selection table (best-for + required data shape per type).
`data_text` is the raw file content as a string; only the extension of
`filename` is used to pick a parser. Binary Excel files are not supported by
this transport — export to CSV first.
## Install & run
```bash
pip install -e . # mcp + pandas + numpy (+ openpyxl if you need xlsx via local CLI)
python scripts/fetch_assets.py # one-time ~2.6MB download (ECharts JS + map GeoJSON)
python server.py # stdio transport
```
After the one-time fetch the server is fully offline. Distribution zips ship
the assets pre-bundled, so zip installs can skip the fetch step. Without the
assets, the 29 non-map chart types still work; `map`, `lines`, `wordcloud`
and `liquid` need them.
Or add to your MCP client config:
```json
{
"mcpServers": {
"smart-charts": {
"command": "python",
"args": ["/path/to/smart-charts-mcp/server.py"]
}
}
}
```
## Smoke test
```bash
pip install -r requirements-dev.txt
python test_smoke.py # handshake + tool count + live profile/render round trip
```
## Notes
- Map charts (`map`, `lines`) bundle China province-level and world country
GeoJSON offline. For China maps you are responsible for using compliant,
officially sanctioned map data in your own jurisdiction.
- Regression suite: `python smart_charts/scripts/regression_check.py`.
- License: MIT.
TDQS
Scored across 4 tools
Each tool occupies a clearly distinct slot in the workflow: profile_data analyzes data, list_chart_types is a static reference, render_chart produces output, and doctor checks the environment. There is no plausible case where an agent would mistake one for another.
Three of four tools follow a clean verb_noun pattern (profile_data, list_chart_types, render_chart), with 'doctor' as the single stylistic deviation. That lone noun-style name is still conventional and unambiguous, so the break is minor.
Four tools is lean but well-scoped for a charting server, with each earning its place in a profile-then-render pipeline. The heavy lifting is consolidated into one large render_chart tool, so the surface is slightly thin at the discovery/utility end but not problematic.
The core lifecycle (preflight, inspect data, choose chart, render HTML) is fully covered, and render_chart itself is extremely broad with 30+ chart types and many options. Minor gaps exist: no tool to persist/export the rendered HTML to a file or to list available datasets, but agents can work around these.