graphein-mcp 0.9.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,6 +2,7 @@
2
2
  import {
3
3
  validateSpec,
4
4
  repairSpec,
5
+ recommendChart,
5
6
  summarize
6
7
  } from "graphein";
7
8
  import { renderChart } from "@graphein/node";
@@ -14,6 +15,9 @@ function json(value) {
14
15
  function specType(spec) {
15
16
  return typeof spec === "object" && spec !== null && "type" in spec ? String(spec.type) : "(missing)";
16
17
  }
18
+ function isRecommendIntent(value) {
19
+ return value === void 0 || value === "trend" || value === "comparison" || value === "distribution" || value === "relationship" || value === "composition";
20
+ }
17
21
  function tidyError(e) {
18
22
  const out = { path: e.path, message: e.message };
19
23
  if (e.rule) out.rule = e.rule;
@@ -109,6 +113,28 @@ function validateChartHandler(args) {
109
113
  ]
110
114
  };
111
115
  }
116
+ function recommendChartHandler(args) {
117
+ if (!isRecommendIntent(args.intent)) {
118
+ return {
119
+ isError: true,
120
+ content: [
121
+ json({
122
+ ok: false,
123
+ message: "Unsupported intent. Expected trend, comparison, distribution, relationship, or composition."
124
+ })
125
+ ]
126
+ };
127
+ }
128
+ return {
129
+ isError: false,
130
+ content: [
131
+ json({
132
+ ok: true,
133
+ recommendations: recommendChart(args.data, { intent: args.intent, maxResults: args.maxResults })
134
+ })
135
+ ]
136
+ };
137
+ }
112
138
  function repairChartHandler(args) {
113
139
  const { spec, applied, remaining } = repairSpec(args.spec);
114
140
  return {
@@ -232,6 +258,19 @@ function createServer() {
232
258
  },
233
259
  async (args) => validateChartHandler(args)
234
260
  );
261
+ server.registerTool(
262
+ "recommend_chart",
263
+ {
264
+ title: "Recommend Graphein chart specs",
265
+ description: "Profile tidy rows and return ranked, ready-to-render ChartSpecs with rationale. Use this before guessing a chart type or encoding.",
266
+ inputSchema: {
267
+ data: z.array(z.record(z.string(), z.any())),
268
+ intent: z.string().optional(),
269
+ maxResults: z.number().int().positive().optional()
270
+ }
271
+ },
272
+ async (args) => recommendChartHandler(args)
273
+ );
235
274
  server.registerTool(
236
275
  "repair_chart",
237
276
  {
@@ -308,4 +347,4 @@ export {
308
347
  VERSION,
309
348
  createServer
310
349
  };
311
- //# sourceMappingURL=chunk-LBB3C2XH.js.map
350
+ //# sourceMappingURL=chunk-PHH2HU3O.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/handlers.ts","../src/resources.ts","../src/create-server.ts"],"sourcesContent":["/**\n * Pure tool handlers — the generate → validate → repair → render → critique loop\n * exposed as plain functions so they can be unit-tested without a transport. Each\n * returns MCP-shaped content (`text` and/or `image` blocks). `createServer` wires\n * these into the `McpServer`.\n *\n * The handlers wrap `graphein`'s self-validating / self-repairing / self-explaining\n * core (`validateSpec`, `repairSpec`, `summarize`) and `@graphein/node`'s headless\n * `renderChart`, so an agent gets the chart **plus** a vision-free critique in one\n * call — and a one-step repair when its spec is slightly wrong.\n */\nimport {\n validateSpec,\n repairSpec,\n recommendChart,\n summarize,\n type ChartSpec,\n type RecommendOptions,\n type ValidationError,\n} from 'graphein';\nimport { renderChart } from '@graphein/node';\nimport type { JsonPatchOp } from './types.js';\n\n/** A subset of MCP content blocks the handlers emit. */\nexport type McpContent =\n | { type: 'text'; text: string }\n | { type: 'image'; data: string; mimeType: string };\n\n/**\n * The shape every handler returns — assignable to the SDK's `CallToolResult`\n * (which is an open/passthrough type, hence the index signature).\n */\nexport interface ToolResult {\n content: McpContent[];\n isError?: boolean;\n [key: string]: unknown;\n}\n\n/** Options accepted by {@link renderChartHandler}. */\nexport interface RenderArgs {\n spec: unknown;\n width?: number;\n height?: number;\n dpr?: number;\n /** Auto-apply safe repairs before rendering when the spec is invalid. Default true. */\n repair?: boolean;\n}\n\nexport interface RecommendChartArgs {\n data: Record<string, unknown>[];\n intent?: string;\n maxResults?: number;\n}\n\nfunction text(value: string): McpContent {\n return { type: 'text', text: value };\n}\n\nfunction json(value: unknown): McpContent {\n return text(JSON.stringify(value, null, 2));\n}\n\nfunction specType(spec: unknown): string {\n return typeof spec === 'object' && spec !== null && 'type' in spec\n ? String((spec as { type: unknown }).type)\n : '(missing)';\n}\n\nfunction isRecommendIntent(value: string | undefined): value is RecommendOptions['intent'] {\n return (\n value === undefined ||\n value === 'trend' ||\n value === 'comparison' ||\n value === 'distribution' ||\n value === 'relationship' ||\n value === 'composition'\n );\n}\n\n/** Slim a validation error for an agent payload (drops nothing useful). */\nfunction tidyError(e: ValidationError) {\n const out: Record<string, unknown> = { path: e.path, message: e.message };\n if (e.rule) out.rule = e.rule;\n if (e.severity) out.severity = e.severity;\n if (e.fix) out.fix = e.fix;\n if (e.suggestion) out.suggestion = e.suggestion;\n return out;\n}\n\n/**\n * **The flagship.** Validate a spec, auto-repair it if it's safely fixable, render\n * it to a PNG headless, and return the image alongside a machine-readable critique\n * (the render report + lint warnings + any repairs applied). When the spec can't be\n * made valid, returns the structured errors and JSON-Patch fixes instead of an image\n * so the agent corrects in one step rather than regenerating.\n */\nexport function renderChartHandler(args: RenderArgs): ToolResult {\n const { spec, width, height, dpr, repair = true } = args;\n\n let working: unknown = spec;\n let validation = validateSpec(working);\n let repairs: JsonPatchOp[] = [];\n\n if (!validation.valid && repair) {\n const repaired = repairSpec(working);\n if (repaired.applied.length > 0) {\n working = repaired.spec;\n repairs = repaired.applied;\n validation = validateSpec(working);\n }\n }\n\n if (!validation.valid) {\n return {\n isError: true,\n content: [\n json({\n ok: false,\n rendered: false,\n stage: 'validate',\n type: specType(working),\n errors: validation.errors.map(tidyError),\n lint: validation.warnings.map(tidyError),\n repairsApplied: repairs,\n hint: 'Apply each error.fix JSON Patch (or the repair_chart tool), then call render_chart again. See the graphein://agent-guide and graphein://schema resources.',\n }),\n ],\n };\n }\n\n const type = specType(working);\n\n try {\n const { png, report, width: pxW, height: pxH } = renderChart(working as ChartSpec, {\n width,\n height,\n dpr,\n });\n return {\n isError: false,\n content: [\n { type: 'image', data: png.toString('base64'), mimeType: 'image/png' },\n json({\n ok: report.ok,\n rendered: true,\n type,\n pixelSize: { width: pxW, height: pxH },\n summary: report.summary,\n marks: report.markCount,\n series: report.seriesCount,\n colors: report.colorCount,\n diagnostics: report.diagnostics,\n lint: validation.warnings.map(tidyError),\n repairsApplied: repairs,\n }),\n ],\n };\n } catch (e) {\n return {\n isError: true,\n content: [\n json({\n ok: false,\n rendered: false,\n stage: 'render',\n type,\n message: e instanceof Error ? e.message : String(e),\n summary: summarize(working as ChartSpec) || undefined,\n repairsApplied: repairs,\n }),\n ],\n };\n }\n}\n\n/**\n * Validate a spec without rendering — fast structural + best-practice feedback.\n * Returns errors (each with a JSON-Patch `fix` when unambiguous and \"did you mean\"\n * suggestions) and lint `warnings`.\n */\nexport function validateChartHandler(args: { spec: unknown }): ToolResult {\n const result = validateSpec(args.spec);\n return {\n isError: false,\n content: [\n json({\n valid: result.valid,\n type: specType(args.spec),\n errors: result.errors.map(tidyError),\n warnings: result.warnings.map(tidyError),\n }),\n ],\n };\n}\n\n/** Recommend ready-to-render ChartSpecs from tidy rows and an optional intent. */\nexport function recommendChartHandler(args: RecommendChartArgs): ToolResult {\n if (!isRecommendIntent(args.intent)) {\n return {\n isError: true,\n content: [\n json({\n ok: false,\n message: 'Unsupported intent. Expected trend, comparison, distribution, relationship, or composition.',\n }),\n ],\n };\n }\n return {\n isError: false,\n content: [\n json({\n ok: true,\n recommendations: recommendChart(args.data, { intent: args.intent, maxResults: args.maxResults }),\n }),\n ],\n };\n}\n\n/**\n * Apply every safe, unambiguous repair Graphein proposes and return the corrected\n * spec, the JSON Patch operations applied, and whether it is now valid.\n */\nexport function repairChartHandler(args: { spec: unknown }): ToolResult {\n const { spec, applied, remaining } = repairSpec(args.spec);\n return {\n isError: false,\n content: [\n json({\n valid: remaining.length === 0,\n applied,\n remaining: remaining.map(tidyError),\n spec,\n }),\n ],\n };\n}\n\n/**\n * Return a deterministic, plain-English summary of what the chart's data shows —\n * doubles as alt-text, needs no LLM.\n */\nexport function summarizeChartHandler(args: { spec: unknown }): ToolResult {\n const result = validateSpec(args.spec);\n if (!result.valid) {\n return {\n isError: true,\n content: [\n json({\n summary: null,\n reason: 'Spec is invalid; fix it first (validate_chart / repair_chart).',\n errors: result.errors.map(tidyError),\n }),\n ],\n };\n }\n const summary = summarize(args.spec as ChartSpec);\n return {\n isError: false,\n content: [\n text(summary || `(No narrative summary is available for a '${specType(args.spec)}' chart.)`),\n ],\n };\n}\n","/**\n * The agent-facing knowledge Graphein serves as MCP **resources** — the schema\n * and the prose guides — so a model that has never seen Graphein's API can read\n * the contract at runtime instead of relying on training data. This is the core\n * of the MCP server's \"neutralize the training-data gap\" purpose.\n *\n * The files live in `../resources/` (committed copies of the repo's `docs/`, kept\n * in sync by `scripts/sync-resources.mjs`). They are read lazily and cached so the\n * server pays nothing until an agent actually asks for one.\n */\nimport { readFileSync } from 'node:fs';\nimport { fileURLToPath } from 'node:url';\n\n/** Metadata describing one served resource. */\nexport interface GrapheinResource {\n /** Short registration name. */\n name: string;\n /** Stable `graphein://…` URI an agent reads. */\n uri: string;\n /** Human title. */\n title: string;\n /** What the resource is and when to read it. */\n description: string;\n /** IANA media type. */\n mimeType: string;\n /** File under `resources/`. */\n file: string;\n}\n\n/** Every resource the server exposes, in the order they should be advertised. */\nexport const RESOURCES: GrapheinResource[] = [\n {\n name: 'schema',\n uri: 'graphein://schema',\n title: 'Graphein ChartSpec JSON Schema',\n description:\n 'The machine-readable JSON Schema for every Graphein ChartSpec and DashboardSpec field — chart types, channels, transforms, annotations, and required properties. Generate or check a spec against this.',\n mimeType: 'application/json',\n file: 'chart-spec.schema.json',\n },\n {\n name: 'agent-guide',\n uri: 'graphein://agent-guide',\n title: 'Graphein Agent Guide',\n description:\n 'A task-oriented guide for producing correct Graphein charts: the one-rule workflow, choosing a chart type, encodings, transforms, the validate → repair → render → critique loop, and worked recipes. Read this first.',\n mimeType: 'text/markdown',\n file: 'agent-guide.md',\n },\n {\n name: 'spec-reference',\n uri: 'graphein://spec-reference',\n title: 'Graphein Spec Reference',\n description:\n 'The exhaustive field-by-field reference for every chart type, channel, transform, annotation, and modifier. Consult this for the precise shape of a specific field.',\n mimeType: 'text/markdown',\n file: 'spec-reference.md',\n },\n];\n\nconst cache = new Map<string, string>();\n\n/**\n * Read a bundled resource file's text, cached. Resolved relative to this module\n * so it works both from `dist/` (published) and `src/` (tests) — the committed\n * `resources/` dir always sits one level up from either.\n */\nexport function readResourceFile(file: string): string {\n const hit = cache.get(file);\n if (hit !== undefined) return hit;\n const url = new URL(`../resources/${file}`, import.meta.url);\n const text = readFileSync(fileURLToPath(url), 'utf8');\n cache.set(file, text);\n return text;\n}\n\n/** Look up a resource by its `graphein://…` URI. */\nexport function resourceByUri(uri: string): GrapheinResource | undefined {\n return RESOURCES.find((r) => r.uri === uri);\n}\n","/**\n * `createServer()` — builds the Graphein MCP server: the generate → validate →\n * repair → render → critique loop as four tools, the schema + guides as resources,\n * and a `create_chart` prompt that teaches the workflow. Split from the stdio\n * entry point (`server.ts`) so it can be driven over any transport — including the\n * in-memory transport in tests.\n */\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport {\n renderChartHandler,\n validateChartHandler,\n repairChartHandler,\n recommendChartHandler,\n summarizeChartHandler,\n} from './handlers.js';\nimport { RESOURCES, readResourceFile } from './resources.js';\n\n/** Package version, surfaced as the MCP server version. */\nexport const VERSION = '0.3.0';\n\nconst SERVER_INSTRUCTIONS = `Graphein is an agent-first data-visualization library: you describe a chart as one JSON ChartSpec ({ type, data, encoding, ... }) and it renders. This server lets you build correct charts without prior knowledge of the API.\n\nWorkflow:\n1. Read the graphein://agent-guide resource (and graphein://schema for exact fields) if you are unsure of the API.\n2. Shape your data as a tidy array — one row per observation, one column per variable.\n3. Emit a ChartSpec and call render_chart: it validates, auto-repairs safe mistakes, renders a PNG, and returns a vision-free critique (the render report + lint warnings). Read the critique to verify the chart looks right.\n4. If a spec is invalid, render_chart (and validate_chart) return structured errors each with a JSON-Patch 'fix' — apply it (or call repair_chart) and retry instead of regenerating.\nUse summarize_chart for deterministic alt-text. Every type rasterizes headlessly, including kpi, table, matrix, slicers and dashboard (static canvas snapshots).`;\n\n/** A permissive object schema for a Graphein spec — validateSpec does the real checking. */\nconst specSchema = z\n .record(z.string(), z.unknown())\n .describe(\n 'A Graphein ChartSpec or DashboardSpec object, e.g. { \"type\": \"line\", \"data\": [...], \"encoding\": {...} }. See the graphein://schema and graphein://agent-guide resources.',\n );\n\n/**\n * Build a fully-configured Graphein MCP server. The caller connects it to a\n * transport (`server.connect(transport)`).\n */\nexport function createServer(): McpServer {\n const server = new McpServer(\n { name: 'graphein-mcp', version: VERSION },\n { instructions: SERVER_INSTRUCTIONS },\n );\n\n // --- Tools: the runtime loop -------------------------------------------------\n\n server.registerTool(\n 'render_chart',\n {\n title: 'Render a Graphein chart',\n description:\n 'The one-call loop: validate a ChartSpec, auto-repair safe mistakes, render it to a PNG, and return the image plus a vision-free critique (render report, lint warnings, repairs applied). If the spec cannot be made valid, returns structured errors with JSON-Patch fixes instead of an image. Every type rasterizes headlessly — kpi, table, matrix, slicers and dashboard render static canvas snapshots.',\n inputSchema: {\n spec: specSchema,\n width: z.number().int().positive().optional().describe('Logical width in CSS px (default 800).'),\n height: z.number().int().positive().optional().describe('Logical height in CSS px (default 500).'),\n dpr: z.number().positive().optional().describe('Device pixel ratio for crisp output (default 2).'),\n repair: z\n .boolean()\n .optional()\n .describe('Auto-apply safe repairs before rendering when the spec is invalid (default true).'),\n },\n },\n async (args) => renderChartHandler(args),\n );\n\n server.registerTool(\n 'validate_chart',\n {\n title: 'Validate a Graphein chart spec',\n description:\n 'Validate a ChartSpec without rendering. Returns structural errors (each with a JSON-Patch `fix` when unambiguous, plus \"did you mean\" suggestions) and best-practice lint warnings. Fast feedback before rendering.',\n inputSchema: { spec: specSchema },\n },\n async (args) => validateChartHandler(args),\n );\n\n server.registerTool(\n 'recommend_chart',\n {\n title: 'Recommend Graphein chart specs',\n description:\n 'Profile tidy rows and return ranked, ready-to-render ChartSpecs with rationale. Use this before guessing a chart type or encoding.',\n inputSchema: {\n data: z.array(z.record(z.string(), z.any())),\n intent: z.string().optional(),\n maxResults: z.number().int().positive().optional(),\n },\n },\n async (args) => recommendChartHandler(args),\n );\n\n server.registerTool(\n 'repair_chart',\n {\n title: 'Repair a Graphein chart spec',\n description:\n 'Apply every safe, unambiguous fix Graphein proposes (misspelled chart type or enum, a temporal field typed as a category, …) and return the corrected spec, the JSON Patch ops applied, and whether it is now valid. Turns a near-miss into a one-step correction.',\n inputSchema: { spec: specSchema },\n },\n async (args) => repairChartHandler(args),\n );\n\n server.registerTool(\n 'summarize_chart',\n {\n title: 'Summarize a Graphein chart',\n description:\n 'Return a deterministic, plain-English description of what the chart\\'s data shows (e.g. \"Users grew 46% over six months, peaking in June\"). Doubles as alt-text; needs no LLM.',\n inputSchema: { spec: specSchema },\n },\n async (args) => summarizeChartHandler(args),\n );\n\n // --- Resources: deliver the API knowledge at runtime -------------------------\n\n for (const r of RESOURCES) {\n server.registerResource(\n r.name,\n r.uri,\n { title: r.title, description: r.description, mimeType: r.mimeType },\n async (uri) => ({\n contents: [{ uri: uri.href, mimeType: r.mimeType, text: readResourceFile(r.file) }],\n }),\n );\n }\n\n // --- Prompt: teach the workflow ---------------------------------------------\n\n server.registerPrompt(\n 'create_chart',\n {\n title: 'Create a Graphein chart',\n description:\n 'Scaffold the workflow for building a validated Graphein chart from a goal (and optional data).',\n argsSchema: {\n goal: z.string().describe('What the chart should show, e.g. \"monthly active users over the last year\".'),\n data: z\n .string()\n .optional()\n .describe('Optional: the data as a JSON array, or a description of the columns available.'),\n },\n },\n ({ goal, data }) => ({\n messages: [\n {\n role: 'user',\n content: {\n type: 'text',\n text: `Build a Graphein chart for this goal:\\n\\n${goal}\\n${\n data ? `\\nData:\\n${data}\\n` : ''\n }\\nSteps:\\n1. If unsure of the API, read the graphein://agent-guide resource (and graphein://schema for exact fields).\\n2. Shape the data as a tidy array — one row per observation, one column per variable.\\n3. Choose a chart type and write a single ChartSpec ({ type, data, encoding, title }).\\n4. Call render_chart with the spec. Read the returned critique (render report + lint) to confirm it looks right.\\n5. If it reports errors, apply each error.fix patch (or call repair_chart) and render again — do not regenerate from scratch.`,\n },\n },\n ],\n }),\n );\n\n return server;\n}\n"],"mappings":"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","names":["text"]}
package/dist/index.js CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  resourceByUri,
9
9
  summarizeChartHandler,
10
10
  validateChartHandler
11
- } from "./chunk-LBB3C2XH.js";
11
+ } from "./chunk-PHH2HU3O.js";
12
12
  export {
13
13
  RESOURCES,
14
14
  VERSION,
package/dist/server.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  createServer
4
- } from "./chunk-LBB3C2XH.js";
4
+ } from "./chunk-PHH2HU3O.js";
5
5
 
6
6
  // src/server.ts
7
7
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "graphein-mcp",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "type": "module",
5
5
  "description": "Model Context Protocol server for Graphein — wraps generate → validate → repair → render → critique into one tool call, and serves Graphein's schema + agent guide as resources so a model that never saw the API can still build correct charts.",
6
6
  "license": "MIT",
@@ -61,9 +61,9 @@
61
61
  "prepack": "tsup"
62
62
  },
63
63
  "dependencies": {
64
- "@graphein/node": "^0.9.0",
64
+ "@graphein/node": "^0.10.0",
65
65
  "@modelcontextprotocol/sdk": "^1.20.0",
66
- "graphein": "^0.9.0",
66
+ "graphein": "^0.10.0",
67
67
  "zod": "^3.23.0"
68
68
  }
69
69
  }
@@ -475,6 +475,8 @@ specs still round‑trip through `JSON.stringify`. Three optional fields on any
475
475
  - `filter` clauses are a `{ param }` (cross‑filter) or a literal predicate:
476
476
  `{ field, equals }`, `{ field, oneOf }`, `{ field, range:[min,max] }`,
477
477
  `{ field, contains }`. An empty/absent selection matches everything (`empty:'all'`).
478
+ - Set `legend:{ "interactive": true }` on multi-series cartesian charts to let swatch
479
+ clicks publish the **visible** series as a set selection (shift/alt-click isolates).
478
480
 
479
481
  **Slicers** are first‑class visuals that publish a selection from a control:
480
482
 
@@ -586,6 +588,8 @@ unless `layout.navigators:'inline'`. Theme cascades to every view.
586
588
 
587
589
  The default look is **flat and modern** (solid fills, minimal shadows). The built‑in
588
590
  palette is accessible on both light and dark backgrounds.
591
+ Use top-level `"palette": "colorblind"` for Okabe–Ito categorical colors, or
592
+ `"bright"`, `"muted"`, or a custom string array when a chart needs different series colors.
589
593
 
590
594
  ## Hand-drawn ("sketch") mode
591
595
 
@@ -326,6 +326,20 @@
326
326
  "$ref": "#/$defs/ThemeInput",
327
327
  "description": "Theme name ('light' | 'dark') or a partial override object."
328
328
  },
329
+ "palette": {
330
+ "anyOf": [
331
+ {
332
+ "type": "string"
333
+ },
334
+ {
335
+ "type": "array",
336
+ "items": {
337
+ "type": "string"
338
+ }
339
+ }
340
+ ],
341
+ "description": "Named categorical palette ('graphein'|'colorblind'|'bright'|'muted') or an explicit array of series colors."
342
+ },
329
343
  "dimensions": {
330
344
  "$ref": "#/$defs/Dimensions"
331
345
  },
@@ -604,6 +618,20 @@
604
618
  "$ref": "#/$defs/ThemeInput",
605
619
  "description": "Theme name ('light' | 'dark') or a partial override object."
606
620
  },
621
+ "palette": {
622
+ "anyOf": [
623
+ {
624
+ "type": "string"
625
+ },
626
+ {
627
+ "type": "array",
628
+ "items": {
629
+ "type": "string"
630
+ }
631
+ }
632
+ ],
633
+ "description": "Named categorical palette ('graphein'|'colorblind'|'bright'|'muted') or an explicit array of series colors."
634
+ },
607
635
  "dimensions": {
608
636
  "$ref": "#/$defs/Dimensions"
609
637
  },
@@ -885,6 +913,20 @@
885
913
  "$ref": "#/$defs/ThemeInput",
886
914
  "description": "Theme name ('light' | 'dark') or a partial override object."
887
915
  },
916
+ "palette": {
917
+ "anyOf": [
918
+ {
919
+ "type": "string"
920
+ },
921
+ {
922
+ "type": "array",
923
+ "items": {
924
+ "type": "string"
925
+ }
926
+ }
927
+ ],
928
+ "description": "Named categorical palette ('graphein'|'colorblind'|'bright'|'muted') or an explicit array of series colors."
929
+ },
888
930
  "dimensions": {
889
931
  "$ref": "#/$defs/Dimensions"
890
932
  },
@@ -1094,6 +1136,20 @@
1094
1136
  "$ref": "#/$defs/ThemeInput",
1095
1137
  "description": "Theme name ('light' | 'dark') or a partial override object."
1096
1138
  },
1139
+ "palette": {
1140
+ "anyOf": [
1141
+ {
1142
+ "type": "string"
1143
+ },
1144
+ {
1145
+ "type": "array",
1146
+ "items": {
1147
+ "type": "string"
1148
+ }
1149
+ }
1150
+ ],
1151
+ "description": "Named categorical palette ('graphein'|'colorblind'|'bright'|'muted') or an explicit array of series colors."
1152
+ },
1097
1153
  "dimensions": {
1098
1154
  "$ref": "#/$defs/Dimensions"
1099
1155
  },
@@ -1272,6 +1328,20 @@
1272
1328
  "$ref": "#/$defs/ThemeInput",
1273
1329
  "description": "Theme name ('light' | 'dark') or a partial override object."
1274
1330
  },
1331
+ "palette": {
1332
+ "anyOf": [
1333
+ {
1334
+ "type": "string"
1335
+ },
1336
+ {
1337
+ "type": "array",
1338
+ "items": {
1339
+ "type": "string"
1340
+ }
1341
+ }
1342
+ ],
1343
+ "description": "Named categorical palette ('graphein'|'colorblind'|'bright'|'muted') or an explicit array of series colors."
1344
+ },
1275
1345
  "dimensions": {
1276
1346
  "$ref": "#/$defs/Dimensions"
1277
1347
  },
@@ -1555,6 +1625,20 @@
1555
1625
  "$ref": "#/$defs/ThemeInput",
1556
1626
  "description": "Theme name ('light' | 'dark') or a partial override object."
1557
1627
  },
1628
+ "palette": {
1629
+ "anyOf": [
1630
+ {
1631
+ "type": "string"
1632
+ },
1633
+ {
1634
+ "type": "array",
1635
+ "items": {
1636
+ "type": "string"
1637
+ }
1638
+ }
1639
+ ],
1640
+ "description": "Named categorical palette ('graphein'|'colorblind'|'bright'|'muted') or an explicit array of series colors."
1641
+ },
1558
1642
  "dimensions": {
1559
1643
  "$ref": "#/$defs/Dimensions"
1560
1644
  },
@@ -1832,6 +1916,20 @@
1832
1916
  "$ref": "#/$defs/ThemeInput",
1833
1917
  "description": "Theme name ('light' | 'dark') or a partial override object."
1834
1918
  },
1919
+ "palette": {
1920
+ "anyOf": [
1921
+ {
1922
+ "type": "string"
1923
+ },
1924
+ {
1925
+ "type": "array",
1926
+ "items": {
1927
+ "type": "string"
1928
+ }
1929
+ }
1930
+ ],
1931
+ "description": "Named categorical palette ('graphein'|'colorblind'|'bright'|'muted') or an explicit array of series colors."
1932
+ },
1835
1933
  "dimensions": {
1836
1934
  "$ref": "#/$defs/Dimensions"
1837
1935
  },
@@ -2423,6 +2521,20 @@
2423
2521
  "$ref": "#/$defs/ThemeInput",
2424
2522
  "description": "Theme name ('light' | 'dark') or a partial override object."
2425
2523
  },
2524
+ "palette": {
2525
+ "anyOf": [
2526
+ {
2527
+ "type": "string"
2528
+ },
2529
+ {
2530
+ "type": "array",
2531
+ "items": {
2532
+ "type": "string"
2533
+ }
2534
+ }
2535
+ ],
2536
+ "description": "Named categorical palette ('graphein'|'colorblind'|'bright'|'muted') or an explicit array of series colors."
2537
+ },
2426
2538
  "dimensions": {
2427
2539
  "$ref": "#/$defs/Dimensions"
2428
2540
  },
@@ -2604,6 +2716,20 @@
2604
2716
  "$ref": "#/$defs/ThemeInput",
2605
2717
  "description": "Theme name ('light' | 'dark') or a partial override object."
2606
2718
  },
2719
+ "palette": {
2720
+ "anyOf": [
2721
+ {
2722
+ "type": "string"
2723
+ },
2724
+ {
2725
+ "type": "array",
2726
+ "items": {
2727
+ "type": "string"
2728
+ }
2729
+ }
2730
+ ],
2731
+ "description": "Named categorical palette ('graphein'|'colorblind'|'bright'|'muted') or an explicit array of series colors."
2732
+ },
2607
2733
  "dimensions": {
2608
2734
  "$ref": "#/$defs/Dimensions"
2609
2735
  },
@@ -2762,6 +2888,20 @@
2762
2888
  "$ref": "#/$defs/ThemeInput",
2763
2889
  "description": "Theme name ('light' | 'dark') or a partial override object."
2764
2890
  },
2891
+ "palette": {
2892
+ "anyOf": [
2893
+ {
2894
+ "type": "string"
2895
+ },
2896
+ {
2897
+ "type": "array",
2898
+ "items": {
2899
+ "type": "string"
2900
+ }
2901
+ }
2902
+ ],
2903
+ "description": "Named categorical palette ('graphein'|'colorblind'|'bright'|'muted') or an explicit array of series colors."
2904
+ },
2765
2905
  "dimensions": {
2766
2906
  "$ref": "#/$defs/Dimensions"
2767
2907
  },
@@ -3340,6 +3480,20 @@
3340
3480
  "$ref": "#/$defs/ThemeInput",
3341
3481
  "description": "Theme name ('light' | 'dark') or a partial override object."
3342
3482
  },
3483
+ "palette": {
3484
+ "anyOf": [
3485
+ {
3486
+ "type": "string"
3487
+ },
3488
+ {
3489
+ "type": "array",
3490
+ "items": {
3491
+ "type": "string"
3492
+ }
3493
+ }
3494
+ ],
3495
+ "description": "Named categorical palette ('graphein'|'colorblind'|'bright'|'muted') or an explicit array of series colors."
3496
+ },
3343
3497
  "dimensions": {
3344
3498
  "$ref": "#/$defs/Dimensions"
3345
3499
  },
@@ -3542,6 +3696,20 @@
3542
3696
  "$ref": "#/$defs/ThemeInput",
3543
3697
  "description": "Theme name ('light' | 'dark') or a partial override object."
3544
3698
  },
3699
+ "palette": {
3700
+ "anyOf": [
3701
+ {
3702
+ "type": "string"
3703
+ },
3704
+ {
3705
+ "type": "array",
3706
+ "items": {
3707
+ "type": "string"
3708
+ }
3709
+ }
3710
+ ],
3711
+ "description": "Named categorical palette ('graphein'|'colorblind'|'bright'|'muted') or an explicit array of series colors."
3712
+ },
3545
3713
  "dimensions": {
3546
3714
  "$ref": "#/$defs/Dimensions"
3547
3715
  },
@@ -3846,6 +4014,20 @@
3846
4014
  "$ref": "#/$defs/ThemeInput",
3847
4015
  "description": "Theme name ('light' | 'dark') or a partial override object."
3848
4016
  },
4017
+ "palette": {
4018
+ "anyOf": [
4019
+ {
4020
+ "type": "string"
4021
+ },
4022
+ {
4023
+ "type": "array",
4024
+ "items": {
4025
+ "type": "string"
4026
+ }
4027
+ }
4028
+ ],
4029
+ "description": "Named categorical palette ('graphein'|'colorblind'|'bright'|'muted') or an explicit array of series colors."
4030
+ },
3849
4031
  "dimensions": {
3850
4032
  "$ref": "#/$defs/Dimensions"
3851
4033
  },
@@ -4082,6 +4264,20 @@
4082
4264
  "$ref": "#/$defs/ThemeInput",
4083
4265
  "description": "Theme name ('light' | 'dark') or a partial override object."
4084
4266
  },
4267
+ "palette": {
4268
+ "anyOf": [
4269
+ {
4270
+ "type": "string"
4271
+ },
4272
+ {
4273
+ "type": "array",
4274
+ "items": {
4275
+ "type": "string"
4276
+ }
4277
+ }
4278
+ ],
4279
+ "description": "Named categorical palette ('graphein'|'colorblind'|'bright'|'muted') or an explicit array of series colors."
4280
+ },
4085
4281
  "dimensions": {
4086
4282
  "$ref": "#/$defs/Dimensions"
4087
4283
  },
@@ -4361,6 +4557,20 @@
4361
4557
  "$ref": "#/$defs/ThemeInput",
4362
4558
  "description": "Theme name ('light' | 'dark') or a partial override object."
4363
4559
  },
4560
+ "palette": {
4561
+ "anyOf": [
4562
+ {
4563
+ "type": "string"
4564
+ },
4565
+ {
4566
+ "type": "array",
4567
+ "items": {
4568
+ "type": "string"
4569
+ }
4570
+ }
4571
+ ],
4572
+ "description": "Named categorical palette ('graphein'|'colorblind'|'bright'|'muted') or an explicit array of series colors."
4573
+ },
4364
4574
  "dimensions": {
4365
4575
  "$ref": "#/$defs/Dimensions"
4366
4576
  },
@@ -4514,6 +4724,14 @@
4514
4724
  },
4515
4725
  "title": {
4516
4726
  "type": "string"
4727
+ },
4728
+ "interactive": {
4729
+ "type": "boolean",
4730
+ "description": "Allow clicking legend items to toggle/isolate series; publishes a selection."
4731
+ },
4732
+ "param": {
4733
+ "type": "string",
4734
+ "description": "Selection param to publish legend visibility to; defaults to the series field name."
4517
4735
  }
4518
4736
  },
4519
4737
  "additionalProperties": false
@@ -4548,6 +4766,20 @@
4548
4766
  "$ref": "#/$defs/ThemeInput",
4549
4767
  "description": "Theme name ('light' | 'dark') or a partial override object."
4550
4768
  },
4769
+ "palette": {
4770
+ "anyOf": [
4771
+ {
4772
+ "type": "string"
4773
+ },
4774
+ {
4775
+ "type": "array",
4776
+ "items": {
4777
+ "type": "string"
4778
+ }
4779
+ }
4780
+ ],
4781
+ "description": "Named categorical palette ('graphein'|'colorblind'|'bright'|'muted') or an explicit array of series colors."
4782
+ },
4551
4783
  "dimensions": {
4552
4784
  "$ref": "#/$defs/Dimensions"
4553
4785
  },
@@ -4781,6 +5013,20 @@
4781
5013
  "$ref": "#/$defs/ThemeInput",
4782
5014
  "description": "Theme name ('light' | 'dark') or a partial override object."
4783
5015
  },
5016
+ "palette": {
5017
+ "anyOf": [
5018
+ {
5019
+ "type": "string"
5020
+ },
5021
+ {
5022
+ "type": "array",
5023
+ "items": {
5024
+ "type": "string"
5025
+ }
5026
+ }
5027
+ ],
5028
+ "description": "Named categorical palette ('graphein'|'colorblind'|'bright'|'muted') or an explicit array of series colors."
5029
+ },
4784
5030
  "dimensions": {
4785
5031
  "$ref": "#/$defs/Dimensions"
4786
5032
  },
@@ -5024,6 +5270,20 @@
5024
5270
  "$ref": "#/$defs/ThemeInput",
5025
5271
  "description": "Theme name ('light' | 'dark') or a partial override object."
5026
5272
  },
5273
+ "palette": {
5274
+ "anyOf": [
5275
+ {
5276
+ "type": "string"
5277
+ },
5278
+ {
5279
+ "type": "array",
5280
+ "items": {
5281
+ "type": "string"
5282
+ }
5283
+ }
5284
+ ],
5285
+ "description": "Named categorical palette ('graphein'|'colorblind'|'bright'|'muted') or an explicit array of series colors."
5286
+ },
5027
5287
  "dimensions": {
5028
5288
  "$ref": "#/$defs/Dimensions"
5029
5289
  },
@@ -5306,6 +5566,20 @@
5306
5566
  "$ref": "#/$defs/ThemeInput",
5307
5567
  "description": "Theme name ('light' | 'dark') or a partial override object."
5308
5568
  },
5569
+ "palette": {
5570
+ "anyOf": [
5571
+ {
5572
+ "type": "string"
5573
+ },
5574
+ {
5575
+ "type": "array",
5576
+ "items": {
5577
+ "type": "string"
5578
+ }
5579
+ }
5580
+ ],
5581
+ "description": "Named categorical palette ('graphein'|'colorblind'|'bright'|'muted') or an explicit array of series colors."
5582
+ },
5309
5583
  "dimensions": {
5310
5584
  "$ref": "#/$defs/Dimensions"
5311
5585
  },
@@ -5573,6 +5847,20 @@
5573
5847
  "$ref": "#/$defs/ThemeInput",
5574
5848
  "description": "Theme name ('light' | 'dark') or a partial override object."
5575
5849
  },
5850
+ "palette": {
5851
+ "anyOf": [
5852
+ {
5853
+ "type": "string"
5854
+ },
5855
+ {
5856
+ "type": "array",
5857
+ "items": {
5858
+ "type": "string"
5859
+ }
5860
+ }
5861
+ ],
5862
+ "description": "Named categorical palette ('graphein'|'colorblind'|'bright'|'muted') or an explicit array of series colors."
5863
+ },
5576
5864
  "dimensions": {
5577
5865
  "$ref": "#/$defs/Dimensions"
5578
5866
  },
@@ -5739,6 +6027,20 @@
5739
6027
  "$ref": "#/$defs/ThemeInput",
5740
6028
  "description": "Theme name ('light' | 'dark') or a partial override object."
5741
6029
  },
6030
+ "palette": {
6031
+ "anyOf": [
6032
+ {
6033
+ "type": "string"
6034
+ },
6035
+ {
6036
+ "type": "array",
6037
+ "items": {
6038
+ "type": "string"
6039
+ }
6040
+ }
6041
+ ],
6042
+ "description": "Named categorical palette ('graphein'|'colorblind'|'bright'|'muted') or an explicit array of series colors."
6043
+ },
5742
6044
  "dimensions": {
5743
6045
  "$ref": "#/$defs/Dimensions"
5744
6046
  },
@@ -6004,6 +6306,20 @@
6004
6306
  "$ref": "#/$defs/ThemeInput",
6005
6307
  "description": "Theme name ('light' | 'dark') or a partial override object."
6006
6308
  },
6309
+ "palette": {
6310
+ "anyOf": [
6311
+ {
6312
+ "type": "string"
6313
+ },
6314
+ {
6315
+ "type": "array",
6316
+ "items": {
6317
+ "type": "string"
6318
+ }
6319
+ }
6320
+ ],
6321
+ "description": "Named categorical palette ('graphein'|'colorblind'|'bright'|'muted') or an explicit array of series colors."
6322
+ },
6007
6323
  "dimensions": {
6008
6324
  "$ref": "#/$defs/Dimensions"
6009
6325
  },
@@ -6215,6 +6531,20 @@
6215
6531
  "$ref": "#/$defs/ThemeInput",
6216
6532
  "description": "Theme name ('light' | 'dark') or a partial override object."
6217
6533
  },
6534
+ "palette": {
6535
+ "anyOf": [
6536
+ {
6537
+ "type": "string"
6538
+ },
6539
+ {
6540
+ "type": "array",
6541
+ "items": {
6542
+ "type": "string"
6543
+ }
6544
+ }
6545
+ ],
6546
+ "description": "Named categorical palette ('graphein'|'colorblind'|'bright'|'muted') or an explicit array of series colors."
6547
+ },
6218
6548
  "dimensions": {
6219
6549
  "$ref": "#/$defs/Dimensions"
6220
6550
  },
@@ -6518,6 +6848,20 @@
6518
6848
  "$ref": "#/$defs/ThemeInput",
6519
6849
  "description": "Theme name ('light' | 'dark') or a partial override object."
6520
6850
  },
6851
+ "palette": {
6852
+ "anyOf": [
6853
+ {
6854
+ "type": "string"
6855
+ },
6856
+ {
6857
+ "type": "array",
6858
+ "items": {
6859
+ "type": "string"
6860
+ }
6861
+ }
6862
+ ],
6863
+ "description": "Named categorical palette ('graphein'|'colorblind'|'bright'|'muted') or an explicit array of series colors."
6864
+ },
6521
6865
  "dimensions": {
6522
6866
  "$ref": "#/$defs/Dimensions"
6523
6867
  },
@@ -6794,6 +7138,20 @@
6794
7138
  "$ref": "#/$defs/ThemeInput",
6795
7139
  "description": "Theme name ('light' | 'dark') or a partial override object."
6796
7140
  },
7141
+ "palette": {
7142
+ "anyOf": [
7143
+ {
7144
+ "type": "string"
7145
+ },
7146
+ {
7147
+ "type": "array",
7148
+ "items": {
7149
+ "type": "string"
7150
+ }
7151
+ }
7152
+ ],
7153
+ "description": "Named categorical palette ('graphein'|'colorblind'|'bright'|'muted') or an explicit array of series colors."
7154
+ },
6797
7155
  "dimensions": {
6798
7156
  "$ref": "#/$defs/Dimensions"
6799
7157
  },
@@ -7045,8 +7403,7 @@
7045
7403
  "positive",
7046
7404
  "negative"
7047
7405
  ],
7048
- "additionalProperties": false,
7049
- "description": "Graphein design tokens. Flat, modern aesthetic: solid fills, minimal shadows, restrained radii, and a teal accent. Two built-in themes (light/dark). Token values are literals so this module stays dependency-free; charts combine these with the color module for derived ramps and contrast."
7406
+ "additionalProperties": false
7050
7407
  },
7051
7408
  "ThemeFont": {
7052
7409
  "type": "object",
@@ -7314,6 +7671,20 @@
7314
7671
  "$ref": "#/$defs/ThemeInput",
7315
7672
  "description": "Theme name ('light' | 'dark') or a partial override object."
7316
7673
  },
7674
+ "palette": {
7675
+ "anyOf": [
7676
+ {
7677
+ "type": "string"
7678
+ },
7679
+ {
7680
+ "type": "array",
7681
+ "items": {
7682
+ "type": "string"
7683
+ }
7684
+ }
7685
+ ],
7686
+ "description": "Named categorical palette ('graphein'|'colorblind'|'bright'|'muted') or an explicit array of series colors."
7687
+ },
7317
7688
  "dimensions": {
7318
7689
  "$ref": "#/$defs/Dimensions"
7319
7690
  },
@@ -7624,6 +7995,20 @@
7624
7995
  "$ref": "#/$defs/ThemeInput",
7625
7996
  "description": "Theme name ('light' | 'dark') or a partial override object."
7626
7997
  },
7998
+ "palette": {
7999
+ "anyOf": [
8000
+ {
8001
+ "type": "string"
8002
+ },
8003
+ {
8004
+ "type": "array",
8005
+ "items": {
8006
+ "type": "string"
8007
+ }
8008
+ }
8009
+ ],
8010
+ "description": "Named categorical palette ('graphein'|'colorblind'|'bright'|'muted') or an explicit array of series colors."
8011
+ },
7627
8012
  "dimensions": {
7628
8013
  "$ref": "#/$defs/Dimensions"
7629
8014
  },
@@ -67,10 +67,11 @@ Shared by **all** chart types.
67
67
  | `data` | `Datum[]` | — | Row‑oriented records. Required for every chart/table. |
68
68
  | `transform` | `Transform[]` | — | Declarative pipeline that reshapes `data` **before** charting (aggregate, bin, filter, fold, timeUnit). See [Transforms](#transforms). |
69
69
  | `theme` | `'light' \| 'dark' \| ThemeOverride` | `'light'` | Theme name or a partial override (see [Themes](#themes)). |
70
+ | `palette` | `'graphein' \| 'colorblind' \| 'bright' \| 'muted' \| string[]` | theme palette | Categorical series palette selector, or explicit colors cycled across series. |
70
71
  | `dimensions` | `{ width?, height?, autoResize? }` | responsive | Omit `width`/`height` to fill the container and track resizes. |
71
72
  | `title` | `string \| TitleConfig` | — | `string`, or `{ text, subtitle?, align? }`. |
72
73
  | `description` | `string` | auto | Accessible alt text. Used verbatim as the chart's `aria-label`; auto‑synthesized from type/title/data when omitted (see [Accessibility](#accessibility)). |
73
- | `legend` | `LegendConfig \| boolean` | auto | `false` hides it; `{ show?, position?, title? }`. `position`: `top \| right \| bottom \| left`. |
74
+ | `legend` | `LegendConfig \| boolean` | auto | `false` hides it; `{ show?, position?, title?, interactive?, param? }`. `interactive:true` lets legend swatch clicks publish visible series as `{ kind:'set', field:<series field>, values:[...] }` (shift/alt-click isolates); `param` defaults to the series field. `position`: `top \| right \| bottom \| left`. |
74
75
  | `tooltip` | `TooltipConfig \| boolean` | `true` | `false` (or `{ show: false }`) disables hover tooltips. |
75
76
  | `axes` | `{ x?: AxisConfig, y?: AxisConfig }` | auto | Per‑axis overrides (cartesian charts). |
76
77
  | `animation` | `AnimationConfig \| boolean` | on | Brief entrance on first render. `false` disables; `{ enabled?, duration?, easing? }`. Honors `prefers-reduced-motion` (see [Animation](#animation)). |
@@ -1120,7 +1121,9 @@ with an optional `base`:
1120
1121
  Overridable token groups: `color` (`background`, `surface`, `text`, `textMuted`,
1121
1122
  `axis`, `grid`, `border`, `accent`, `palette[]`, `positive`, `negative`),
1122
1123
  `font`, `spacing`, `radius`, `stroke`. The default categorical `palette` is a
1123
- vibrant, accessibility‑tuned 10‑color set that reads well on light and dark.
1124
+ vibrant, accessibility‑tuned 10‑color set that reads well on light and dark. For
1125
+ one chart, prefer top-level `palette: 'colorblind'` (or `'bright'`, `'muted'`, or
1126
+ an explicit `string[]`) instead of overriding theme tokens.
1124
1127
 
1125
1128
  ---
1126
1129
 
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/handlers.ts","../src/resources.ts","../src/create-server.ts"],"sourcesContent":["/**\n * Pure tool handlers — the generate → validate → repair → render → critique loop\n * exposed as plain functions so they can be unit-tested without a transport. Each\n * returns MCP-shaped content (`text` and/or `image` blocks). `createServer` wires\n * these into the `McpServer`.\n *\n * The handlers wrap `graphein`'s self-validating / self-repairing / self-explaining\n * core (`validateSpec`, `repairSpec`, `summarize`) and `@graphein/node`'s headless\n * `renderChart`, so an agent gets the chart **plus** a vision-free critique in one\n * call — and a one-step repair when its spec is slightly wrong.\n */\nimport {\n validateSpec,\n repairSpec,\n summarize,\n type ChartSpec,\n type ValidationError,\n} from 'graphein';\nimport { renderChart } from '@graphein/node';\nimport type { JsonPatchOp } from './types.js';\n\n/** A subset of MCP content blocks the handlers emit. */\nexport type McpContent =\n | { type: 'text'; text: string }\n | { type: 'image'; data: string; mimeType: string };\n\n/**\n * The shape every handler returns — assignable to the SDK's `CallToolResult`\n * (which is an open/passthrough type, hence the index signature).\n */\nexport interface ToolResult {\n content: McpContent[];\n isError?: boolean;\n [key: string]: unknown;\n}\n\n/** Options accepted by {@link renderChartHandler}. */\nexport interface RenderArgs {\n spec: unknown;\n width?: number;\n height?: number;\n dpr?: number;\n /** Auto-apply safe repairs before rendering when the spec is invalid. Default true. */\n repair?: boolean;\n}\n\nfunction text(value: string): McpContent {\n return { type: 'text', text: value };\n}\n\nfunction json(value: unknown): McpContent {\n return text(JSON.stringify(value, null, 2));\n}\n\nfunction specType(spec: unknown): string {\n return typeof spec === 'object' && spec !== null && 'type' in spec\n ? String((spec as { type: unknown }).type)\n : '(missing)';\n}\n\n/** Slim a validation error for an agent payload (drops nothing useful). */\nfunction tidyError(e: ValidationError) {\n const out: Record<string, unknown> = { path: e.path, message: e.message };\n if (e.rule) out.rule = e.rule;\n if (e.severity) out.severity = e.severity;\n if (e.fix) out.fix = e.fix;\n if (e.suggestion) out.suggestion = e.suggestion;\n return out;\n}\n\n/**\n * **The flagship.** Validate a spec, auto-repair it if it's safely fixable, render\n * it to a PNG headless, and return the image alongside a machine-readable critique\n * (the render report + lint warnings + any repairs applied). When the spec can't be\n * made valid, returns the structured errors and JSON-Patch fixes instead of an image\n * so the agent corrects in one step rather than regenerating.\n */\nexport function renderChartHandler(args: RenderArgs): ToolResult {\n const { spec, width, height, dpr, repair = true } = args;\n\n let working: unknown = spec;\n let validation = validateSpec(working);\n let repairs: JsonPatchOp[] = [];\n\n if (!validation.valid && repair) {\n const repaired = repairSpec(working);\n if (repaired.applied.length > 0) {\n working = repaired.spec;\n repairs = repaired.applied;\n validation = validateSpec(working);\n }\n }\n\n if (!validation.valid) {\n return {\n isError: true,\n content: [\n json({\n ok: false,\n rendered: false,\n stage: 'validate',\n type: specType(working),\n errors: validation.errors.map(tidyError),\n lint: validation.warnings.map(tidyError),\n repairsApplied: repairs,\n hint: 'Apply each error.fix JSON Patch (or the repair_chart tool), then call render_chart again. See the graphein://agent-guide and graphein://schema resources.',\n }),\n ],\n };\n }\n\n const type = specType(working);\n\n try {\n const { png, report, width: pxW, height: pxH } = renderChart(working as ChartSpec, {\n width,\n height,\n dpr,\n });\n return {\n isError: false,\n content: [\n { type: 'image', data: png.toString('base64'), mimeType: 'image/png' },\n json({\n ok: report.ok,\n rendered: true,\n type,\n pixelSize: { width: pxW, height: pxH },\n summary: report.summary,\n marks: report.markCount,\n series: report.seriesCount,\n colors: report.colorCount,\n diagnostics: report.diagnostics,\n lint: validation.warnings.map(tidyError),\n repairsApplied: repairs,\n }),\n ],\n };\n } catch (e) {\n return {\n isError: true,\n content: [\n json({\n ok: false,\n rendered: false,\n stage: 'render',\n type,\n message: e instanceof Error ? e.message : String(e),\n summary: summarize(working as ChartSpec) || undefined,\n repairsApplied: repairs,\n }),\n ],\n };\n }\n}\n\n/**\n * Validate a spec without rendering — fast structural + best-practice feedback.\n * Returns errors (each with a JSON-Patch `fix` when unambiguous and \"did you mean\"\n * suggestions) and lint `warnings`.\n */\nexport function validateChartHandler(args: { spec: unknown }): ToolResult {\n const result = validateSpec(args.spec);\n return {\n isError: false,\n content: [\n json({\n valid: result.valid,\n type: specType(args.spec),\n errors: result.errors.map(tidyError),\n warnings: result.warnings.map(tidyError),\n }),\n ],\n };\n}\n\n/**\n * Apply every safe, unambiguous repair Graphein proposes and return the corrected\n * spec, the JSON Patch operations applied, and whether it is now valid.\n */\nexport function repairChartHandler(args: { spec: unknown }): ToolResult {\n const { spec, applied, remaining } = repairSpec(args.spec);\n return {\n isError: false,\n content: [\n json({\n valid: remaining.length === 0,\n applied,\n remaining: remaining.map(tidyError),\n spec,\n }),\n ],\n };\n}\n\n/**\n * Return a deterministic, plain-English summary of what the chart's data shows —\n * doubles as alt-text, needs no LLM.\n */\nexport function summarizeChartHandler(args: { spec: unknown }): ToolResult {\n const result = validateSpec(args.spec);\n if (!result.valid) {\n return {\n isError: true,\n content: [\n json({\n summary: null,\n reason: 'Spec is invalid; fix it first (validate_chart / repair_chart).',\n errors: result.errors.map(tidyError),\n }),\n ],\n };\n }\n const summary = summarize(args.spec as ChartSpec);\n return {\n isError: false,\n content: [\n text(summary || `(No narrative summary is available for a '${specType(args.spec)}' chart.)`),\n ],\n };\n}\n","/**\n * The agent-facing knowledge Graphein serves as MCP **resources** — the schema\n * and the prose guides — so a model that has never seen Graphein's API can read\n * the contract at runtime instead of relying on training data. This is the core\n * of the MCP server's \"neutralize the training-data gap\" purpose.\n *\n * The files live in `../resources/` (committed copies of the repo's `docs/`, kept\n * in sync by `scripts/sync-resources.mjs`). They are read lazily and cached so the\n * server pays nothing until an agent actually asks for one.\n */\nimport { readFileSync } from 'node:fs';\nimport { fileURLToPath } from 'node:url';\n\n/** Metadata describing one served resource. */\nexport interface GrapheinResource {\n /** Short registration name. */\n name: string;\n /** Stable `graphein://…` URI an agent reads. */\n uri: string;\n /** Human title. */\n title: string;\n /** What the resource is and when to read it. */\n description: string;\n /** IANA media type. */\n mimeType: string;\n /** File under `resources/`. */\n file: string;\n}\n\n/** Every resource the server exposes, in the order they should be advertised. */\nexport const RESOURCES: GrapheinResource[] = [\n {\n name: 'schema',\n uri: 'graphein://schema',\n title: 'Graphein ChartSpec JSON Schema',\n description:\n 'The machine-readable JSON Schema for every Graphein ChartSpec and DashboardSpec field — chart types, channels, transforms, annotations, and required properties. Generate or check a spec against this.',\n mimeType: 'application/json',\n file: 'chart-spec.schema.json',\n },\n {\n name: 'agent-guide',\n uri: 'graphein://agent-guide',\n title: 'Graphein Agent Guide',\n description:\n 'A task-oriented guide for producing correct Graphein charts: the one-rule workflow, choosing a chart type, encodings, transforms, the validate → repair → render → critique loop, and worked recipes. Read this first.',\n mimeType: 'text/markdown',\n file: 'agent-guide.md',\n },\n {\n name: 'spec-reference',\n uri: 'graphein://spec-reference',\n title: 'Graphein Spec Reference',\n description:\n 'The exhaustive field-by-field reference for every chart type, channel, transform, annotation, and modifier. Consult this for the precise shape of a specific field.',\n mimeType: 'text/markdown',\n file: 'spec-reference.md',\n },\n];\n\nconst cache = new Map<string, string>();\n\n/**\n * Read a bundled resource file's text, cached. Resolved relative to this module\n * so it works both from `dist/` (published) and `src/` (tests) — the committed\n * `resources/` dir always sits one level up from either.\n */\nexport function readResourceFile(file: string): string {\n const hit = cache.get(file);\n if (hit !== undefined) return hit;\n const url = new URL(`../resources/${file}`, import.meta.url);\n const text = readFileSync(fileURLToPath(url), 'utf8');\n cache.set(file, text);\n return text;\n}\n\n/** Look up a resource by its `graphein://…` URI. */\nexport function resourceByUri(uri: string): GrapheinResource | undefined {\n return RESOURCES.find((r) => r.uri === uri);\n}\n","/**\n * `createServer()` — builds the Graphein MCP server: the generate → validate →\n * repair → render → critique loop as four tools, the schema + guides as resources,\n * and a `create_chart` prompt that teaches the workflow. Split from the stdio\n * entry point (`server.ts`) so it can be driven over any transport — including the\n * in-memory transport in tests.\n */\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\nimport {\n renderChartHandler,\n validateChartHandler,\n repairChartHandler,\n summarizeChartHandler,\n} from './handlers.js';\nimport { RESOURCES, readResourceFile } from './resources.js';\n\n/** Package version, surfaced as the MCP server version. */\nexport const VERSION = '0.3.0';\n\nconst SERVER_INSTRUCTIONS = `Graphein is an agent-first data-visualization library: you describe a chart as one JSON ChartSpec ({ type, data, encoding, ... }) and it renders. This server lets you build correct charts without prior knowledge of the API.\n\nWorkflow:\n1. Read the graphein://agent-guide resource (and graphein://schema for exact fields) if you are unsure of the API.\n2. Shape your data as a tidy array — one row per observation, one column per variable.\n3. Emit a ChartSpec and call render_chart: it validates, auto-repairs safe mistakes, renders a PNG, and returns a vision-free critique (the render report + lint warnings). Read the critique to verify the chart looks right.\n4. If a spec is invalid, render_chart (and validate_chart) return structured errors each with a JSON-Patch 'fix' — apply it (or call repair_chart) and retry instead of regenerating.\nUse summarize_chart for deterministic alt-text. Every type rasterizes headlessly, including kpi, table, matrix, slicers and dashboard (static canvas snapshots).`;\n\n/** A permissive object schema for a Graphein spec — validateSpec does the real checking. */\nconst specSchema = z\n .record(z.string(), z.unknown())\n .describe(\n 'A Graphein ChartSpec or DashboardSpec object, e.g. { \"type\": \"line\", \"data\": [...], \"encoding\": {...} }. See the graphein://schema and graphein://agent-guide resources.',\n );\n\n/**\n * Build a fully-configured Graphein MCP server. The caller connects it to a\n * transport (`server.connect(transport)`).\n */\nexport function createServer(): McpServer {\n const server = new McpServer(\n { name: 'graphein-mcp', version: VERSION },\n { instructions: SERVER_INSTRUCTIONS },\n );\n\n // --- Tools: the runtime loop -------------------------------------------------\n\n server.registerTool(\n 'render_chart',\n {\n title: 'Render a Graphein chart',\n description:\n 'The one-call loop: validate a ChartSpec, auto-repair safe mistakes, render it to a PNG, and return the image plus a vision-free critique (render report, lint warnings, repairs applied). If the spec cannot be made valid, returns structured errors with JSON-Patch fixes instead of an image. Every type rasterizes headlessly — kpi, table, matrix, slicers and dashboard render static canvas snapshots.',\n inputSchema: {\n spec: specSchema,\n width: z.number().int().positive().optional().describe('Logical width in CSS px (default 800).'),\n height: z.number().int().positive().optional().describe('Logical height in CSS px (default 500).'),\n dpr: z.number().positive().optional().describe('Device pixel ratio for crisp output (default 2).'),\n repair: z\n .boolean()\n .optional()\n .describe('Auto-apply safe repairs before rendering when the spec is invalid (default true).'),\n },\n },\n async (args) => renderChartHandler(args),\n );\n\n server.registerTool(\n 'validate_chart',\n {\n title: 'Validate a Graphein chart spec',\n description:\n 'Validate a ChartSpec without rendering. Returns structural errors (each with a JSON-Patch `fix` when unambiguous, plus \"did you mean\" suggestions) and best-practice lint warnings. Fast feedback before rendering.',\n inputSchema: { spec: specSchema },\n },\n async (args) => validateChartHandler(args),\n );\n\n server.registerTool(\n 'repair_chart',\n {\n title: 'Repair a Graphein chart spec',\n description:\n 'Apply every safe, unambiguous fix Graphein proposes (misspelled chart type or enum, a temporal field typed as a category, …) and return the corrected spec, the JSON Patch ops applied, and whether it is now valid. Turns a near-miss into a one-step correction.',\n inputSchema: { spec: specSchema },\n },\n async (args) => repairChartHandler(args),\n );\n\n server.registerTool(\n 'summarize_chart',\n {\n title: 'Summarize a Graphein chart',\n description:\n 'Return a deterministic, plain-English description of what the chart\\'s data shows (e.g. \"Users grew 46% over six months, peaking in June\"). Doubles as alt-text; needs no LLM.',\n inputSchema: { spec: specSchema },\n },\n async (args) => summarizeChartHandler(args),\n );\n\n // --- Resources: deliver the API knowledge at runtime -------------------------\n\n for (const r of RESOURCES) {\n server.registerResource(\n r.name,\n r.uri,\n { title: r.title, description: r.description, mimeType: r.mimeType },\n async (uri) => ({\n contents: [{ uri: uri.href, mimeType: r.mimeType, text: readResourceFile(r.file) }],\n }),\n );\n }\n\n // --- Prompt: teach the workflow ---------------------------------------------\n\n server.registerPrompt(\n 'create_chart',\n {\n title: 'Create a Graphein chart',\n description:\n 'Scaffold the workflow for building a validated Graphein chart from a goal (and optional data).',\n argsSchema: {\n goal: z.string().describe('What the chart should show, e.g. \"monthly active users over the last year\".'),\n data: z\n .string()\n .optional()\n .describe('Optional: the data as a JSON array, or a description of the columns available.'),\n },\n },\n ({ goal, data }) => ({\n messages: [\n {\n role: 'user',\n content: {\n type: 'text',\n text: `Build a Graphein chart for this goal:\\n\\n${goal}\\n${\n data ? `\\nData:\\n${data}\\n` : ''\n }\\nSteps:\\n1. If unsure of the API, read the graphein://agent-guide resource (and graphein://schema for exact fields).\\n2. Shape the data as a tidy array — one row per observation, one column per variable.\\n3. Choose a chart type and write a single ChartSpec ({ type, data, encoding, title }).\\n4. Call render_chart with the spec. Read the returned critique (render report + lint) to confirm it looks right.\\n5. If it reports errors, apply each error.fix patch (or call repair_chart) and render again — do not regenerate from scratch.`,\n },\n },\n ],\n }),\n );\n\n return server;\n}\n"],"mappings":"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","names":["text"]}