Local MCP for LLM Chart Authoring

The ggaction package includes a local, read-only MCP server that helps a language model choose current actions and exact call shapes without preloading the complete documentation. It runs over standard input/output on the user’s machine. It does not require a hosted server, account, or authentication.

The server does not execute chart code, render output, read request-selected files, make network requests, or collect telemetry.

Measured impact

A fixed 576-run evaluation compared public-documentation browsing with MCP-first authoring plus bounded fallback across Terra, Luna, and Nano. Each model-condition cell contains 48 observations from the same 24 tasks and two repetitions.

Strict success, tokens, and model calls for Docs only and MCP with fallback across Terra, Luna, and Nano

Across all three models, MCP with bounded fallback raised strict task success from 19.4% to 85.4%, reduced tokens per task from 13,200 to 6,052, and reduced model calls per task from 4.49 to 2.63. Provider failures remain failures in these totals rather than being removed after the run.

These results describe one fixed task set and its model and provider conditions; they are evidence for the authoring route, not a performance guarantee for every request. See the compact benchmark record for the reviewable aggregate data and the complete evaluation record for the condition definitions, paired comparisons, cost boundaries, provider failure analysis, and limitations.

Install and launch

Install ggaction in the project where the MCP client will run:

npm install ggaction

Launch the installed executable from that project:

npx --no-install ggaction-mcp

For an MCP client configuration, prefer the absolute path to the project-local executable so the selected ggaction version is reproducible:

{
  "mcpServers": {
    "ggaction": {
      "command": "/absolute/path/to/project/node_modules/.bin/ggaction-mcp"
    }
  }
}

The exact configuration container varies by MCP client, but the command always starts the same local stdio process. Node.js 20 or later is required.

One-tool workflow

The server exposes exactly one model-visible tool:

search_ggaction({ query })

Send only the exact user request in one query, including chart or mark type, transforms, encodings, guides, layout, and output format when they matter. Do not append dataset contents, code scaffolding, or evaluator instructions:

scatter plot with a color legend at bottom as svg

The result is a bounded JSON task packet with:

The MCP response and the deterministic direct adapter use the same serialized task packet. A packet is never silently truncated; it fails if it exceeds its 6,144-byte hard ceiling.

authoring.prerequisites gives the exact signatures and calls for the common Canvas and data setup that is not already present in actionPlan. Supply every name listed in a prerequisite’s bindings array—currently the caller-owned values array. If the request explicitly asks to create Canvas or data, the corresponding call appears once in authoring.steps and is omitted from authoring.prerequisites. Keep every returned program = ... assignment because each action returns a new immutable ChartProgram. Run authoring.steps in order after those prerequisites:

import { chart } from "ggaction";
import { renderToSVG } from "ggaction/svg";

let program = chart()
program = program.createCanvas({
  width: 800,
  height: 600,
  margin: { top: 140, right: 220, bottom: 120, left: 260 }
})
program = program.createData({ values })
program = program.createScatterPlot({
  x: { field: "x", fieldType: "quantitative" },
  y: { field: "y", fieldType: "quantitative" }
})
const output = renderToSVG(program)

The resolver closes only deterministic runtime dependencies. It can add a required area owner for density, pass a derived dataset to its consumer, bind a unique compatible scale, or name a stable mark target. It does not invent a missing chart, label positions, legend channel, or composition children; those remain explicit unresolved decisions.

Unsupported output and a missing supported renderer are separate decisions. For example, render JPEG reports terminal unsupported.jpg and open renderer.format, but recommends only the bounded renderer-choice section. The unsupported entry itself requires no documentation read. If the same request already names SVG, PNG, PDF, or Browser Canvas, it reports only the terminal unsupported.jpg decision.

Read-only resources

MCP resource discovery provides a small overview, bounded task recipes, and templates for one exact action card. These resources are for selective reads, not a replacement full-documentation preload.

Documentation fallback is stricter. A docs section is readable only when its exact URI appears in an unresolved[].resources list from the latest search_ggaction result. unsupported entries do not unlock documentation. A later result without that URI removes access again. This keeps the default flow compact:

complete request
  → search_ggaction
  → use the task packet when resolved
  → read only the recommended bounded section when unresolved
  → clarify and search again

What the server does not provide

Use the complete action reference manually when a human needs the full contract. For normal model-assisted authoring, start with the one search call and add documentation only for explicit gaps.

Getting Started · Chart Recipes · Rendering · Supported Features