The open chart format an LLM writes and the browser renders — to interactive, accessible SVG.
The MCP exposes Blueprint Chart's dataviz handbook, DSL grammar reference, chart-type docs, and canonical samples as MCP resources, plus eleven deterministic tools: validate_dsl, inspect_dsl, recommend_chart_type, render, list_chart_types, describe_chart_type, get_example, get_grammar, export_chart, search_examples, and list_palettes. Your LLM writes the .bpc; the MCP grounds it in real dataviz pedagogy and gives it a tight feedback loop.
npx @blueprint-chart/mcp # stdio (for Claude Desktop, Claude Code, Cursor)
npx @blueprint-chart/mcp --http # HTTP/SSE on 127.0.0.1:4321
Add to claude_desktop_config.json:
{
"mcpServers": {
"blueprint-chart": {
"command": "npx",
"args": ["-y", "@blueprint-chart/mcp"]
}
}
}
claude mcp add blueprint-chart \
-e BLUEPRINT_CHART_EDITOR_URL=https://blueprintchart.com \
-e BLUEPRINT_CHART_DOCS_URL=https://docs.blueprintchart.com \
-- npx -y @blueprint-chart/mcp
| Tool | Purpose |
|---|---|
validate_dsl | Parse .bpc; returns { valid, errors[], warnings[] } — each error has code, message, suggestion |
inspect_dsl | Parse and summarize: chartType, scenes, seriesCount, rowCount, hasHighlights, hasColorizes, etc. |
recommend_chart_type | Rank chart types for a given column shape and row count |
render | Render to SVG (default), PNG, or HTML; with format:"png" returns an inline image both you and the user can see. Always returns structured frame metadata. When MCP_PUBLIC_URL is set, includes urls ({png,svg,bpc}) — stateless links where the chart data travels inside the URL. Set modelVisible:false to drop the inline image from the response entirely, spending no image tokens. Pass save:<path> to write the output to disk (requires MCP_FS_WRITE_DIR; writes are confined to that directory). Width/height capped at 1600; PNG is 2× retina. |
list_chart_types | List all renderable chart types (tool equivalent of bpc://handbook/choosing) |
describe_chart_type | Properties, when-to-use, when-NOT-to-use, and data-shape for one chart type (tool equivalent of bpc://chart-types/{slug}) |
get_example | Fetch a canonical .bpc sample by chart type or sample name (tool equivalent of bpc://samples/{id}) |
search_examples | Find canonical examples by topic keywords and/or chart type (returns pointers; fetch full DSL with get_example) |
get_grammar | Full DSL syntax reference (tool equivalent of bpc://grammar) |
list_palettes | List named colour palettes with hex colours for colorPalette |
export_chart | Validate a .bpc and return shareable URLs plus an inline preview. Returns { copyUrl, embedUrl, urls?, frame } — copyUrl is editable in the editor, embedUrl is a read-only iframe target, urls.{png,svg,bpc} are stateless rendered/source links (when MCP_PUBLIC_URL is set). Set modelVisible:false to drop the inline preview from the response entirely, spending no image tokens. Requires BLUEPRINT_CHART_EDITOR_URL; preview failures never block the export. |
The discovery tools (list_chart_types, describe_chart_type, get_example, search_examples, get_grammar, list_palettes) let clients without MCP resource support access the same reference material that the bpc:// URIs expose.
The render tool can write its output to disk via save: <path>. This is disabled by default. Set MCP_FS_WRITE_DIR to a directory to enable it — ideally an absolute path; a relative value is resolved from the server's working directory at startup. Every write lands inside that directory (a sandbox), so you never have to worry about where a client puts files: relative save paths are joined to it, an absolute path already inside it is used as-is, and any other absolute path is re-anchored under it (the leading slash is stripped and the rest joined on, so save: "/tmp/foo.png" becomes <dir>/tmp/foo.png). Only paths that still escape via ../ traversal are rejected. Missing subdirectories are created automatically. Containment is checked lexically (no realpath), so a symlink whose lexical path is inside the sandbox still passes the check and is then resolved by the OS at write time — if its target is outside the sandbox, the write reaches it. Avoid placing symlinks in the sandbox if isolation matters to you.
Add the -e flag to your claude mcp add command:
claude mcp add blueprint-chart \
-e MCP_FS_WRITE_DIR=/path/to/output \
-- npx -y @blueprint-chart/mcp
bpc://grammar — full DSL syntax referencebpc://handbook/<slug> — dataviz pedagogy (choosing, design-principles, color, typography, annotations, accessibility, ...)bpc://guide/<slug> — usage guides (scenes, palettes, data-transforms, ...)bpc://chart-types/<slug> — per-chart-type docsbpc://samples/<id> — canonical .bpc examplesbpc://reference/dsl/<slug>, bpc://reference/api/<slug> — full referenceauthor_chart — primes the LLM end-to-end (read → write → validate → render → iterate)Once the MCP is connected, ask Claude to make a chart:
You: Make a horizontal bar chart of English letter frequencies — top 10, highlight E.
Claude: (calls
list_chart_types,get_example({ chartType: "bar-horizontal" }), writes the.bpc, callsvalidate_dslto confirm it parses, callsrenderwithformat: 'png'and shows you the image and the source)Here's the chart:
![image]
chart bar-horizontal { title = "E is the most frequent letter in English" sort = descending valueLabels = true highlight "E" data { "E" = 12.70; "T" = 9.06; "A" = 8.17; ... } }
The MCP grounds Claude in real dataviz pedagogy (the handbook) before it writes a single line of DSL, then closes the loop with deterministic parse + render feedback.
.bpc looks likechart bar-vertical {
title = "E is the most frequent letter in English"
description = "How often each letter appears in typical English text"
source = "Lewand, Cryptological Mathematics"
colorPalette = "London"
sort = descending
valueLabels = true
highlight "E"
data {
"E" = 12.70
"T" = 9.06
"A" = 8.17
"O" = 7.51
...
}
}
Full grammar at bpc://grammar; 17 canonical samples at bpc://samples/<id> (letter-frequency, co2-emissions, quarterly-revenue, browser-market, temperature-anomaly, population-stacked-bar, ...).
validate_dsl — parse with structured diagnosticsRequest:
{
"name": "validate_dsl",
"arguments": { "source": "chart bar-vertical {\n title = \"oops\n}" }
}
Response — valid is false; each entry in errors[] carries a code, human-readable message, and an actionable suggestion:
{
"valid": false,
"errors": [
{
"code": "E_PARSE",
"message": "Expected \"\\\"\" but end of input found.",
"suggestion": "Close the string literal on line 2."
}
],
"warnings": []
}
inspect_dsl — structured summaryRequest:
{ "name": "inspect_dsl", "arguments": { "source": "<.bpc source>" } }
Response:
{
"ok": true,
"data": {
"chartType": "bar-vertical",
"scenes": [{ "index": 1, "hasTransition": false }],
"hasAnnotations": false,
"hasColorizes": false,
"hasHighlights": true,
"hasAreaFills": false,
"seriesCount": 0,
"rowCount": 26
}
}
recommend_chart_type — ranked suggestionsRequest:
{
"name": "recommend_chart_type",
"arguments": { "columnTypes": ["date", "number", "number", "number"], "rowCount": 24 }
}
Response:
{
"ok": true,
"data": {
"recommendations": [
{ "chartType": "line-multi", "label": "Multi-Line Chart", "fitness": "best",
"reason": "1 date + 3 numeric columns — compare trends" },
{ "chartType": "bar-multi", "label": "Grouped Bar Chart", "fitness": "alternative",
"reason": "Can also show as grouped bars" }
]
}
}
render — SVG (default), PNG, or HTMLRequest:
{
"name": "render",
"arguments": { "source": "<.bpc source>", "format": "png", "width": 800, "height": 500 }
}
Response:
{
"ok": true,
"data": {
"svg": "<svg ...>...</svg>",
"png": "<base64-encoded image>",
"mimeType": "image/png",
"urls": {
"png": "https://mcp.blueprintchart.com/render.png?bpc64=…",
"svg": "https://mcp.blueprintchart.com/render.svg?bpc64=…",
"bpc": "https://mcp.blueprintchart.com/render.bpc?bpc64=…"
}
}
}
The urls field is only present when MCP_PUBLIC_URL is configured; every render and export_chart response then includes these stateless links, with the chart data travelling inside the URL (as bpc64, a URL-safe base64 encoding of the .bpc source) — no session, no server state required. Set modelVisible:false in the request to drop the inline image from the response entirely, spending no image tokens.
If rasterization fails (rare), errors[] is non-empty — each entry has a code ("E_RENDER") and a suggestion — and the response still includes the SVG that was successfully produced, so partial success is preserved.
Embed a chart directly in a page:
<img src="https://<your-mcp-host>/render.png?bpc64=<bpc64value>&width=800&height=500" alt="My chart" width="800" height="500">
/render.bpc serves the raw .bpc source — it's "view source" for any chart URL, handy for sharing or reproducing a chart from its link alone.
Sources whose encoding exceeds 8 KB return 413 from the endpoints (and the tool omits urls, returning urlsOmitted: "source-too-large" instead) — use the inline PNG for very large charts.
{ "uri": "bpc://handbook/choosing" }
Returns the full Markdown of the "Choosing the Right Chart" handbook page (same content as docs.blueprintchart.com).
{ "uri": "bpc://samples/letter-frequency" }
Returns the raw .bpc source for the letter-frequency sample as text/plain — exactly what the LLM should imitate.
MIT
Source-derived launch command. Check the maintainer’s required arguments and credentials before running:
npx -y @blueprint-chart/mcpMerge this template into ~/Library/Application Support/Claude/claude_desktop_config.json. Keep existing servers. Add any arguments, credentials, and permissions required by the maintainer; this template has not been install-tested.
{
"mcpServers": {
"io-github-blueprint-chart-mcp": {
"command": "npx",
"args": [
"-y",
"@blueprint-chart/mcp"
]
}
}
}Restart Claude Desktop completely for changes to take effect. Confirm the server appears connected in the client’s tool list, then try a read-only example from its documentation.
Claude Desktop setup reference@blueprint-chart/mcpnpmBlueprint Chart works with any MCP-compatible client. Copy the config snippet from the Configuration section above and add it to the file shown for your client, then restart the application.
~/Library/Application Support/Claude/claude_desktop_config.jsonRestart Claude Desktop completely for changes to take effect.~/.cursor/mcp.jsonRestart Cursor for changes to take effect..vscode/mcp.jsonReload VS Code window for changes to take effect.~/.codeium/windsurf/mcp_config.jsonRestart Windsurf for changes to take effect..mcp.jsonSave at the project root, then start Claude Code in that project and review the MCP server approval prompt. Keep real credentials out of shared files.